我是靠谱客的博主 雪白鸵鸟,这篇文章主要介绍[UOJ#246][UER#7C]套路(数luan学gao相关),现在分享给大家,希望可以做个参考。

题目描述

传送门

题解

算法一
直接上暴力。。。时间复杂度 O(n4) ,期望得分7分。

算法二
s(l,r) 表示l~r这一段两两差的最小值,那么可以递推: s(l,r)=min(|alar|,min(s(l,r1),s(l+1,r)))
求出 s(l,r) 了之后就可以暴力枚举了。时间复杂度 O(n2) ,期望得分20分。
这里的数组可以开成一维的,不过需要用一个特别的技巧:从后向前递推,数组里保留上一次递推的答案,那么
f[j]=min(f[j],f[j-1]),f[j]=min(f[j],Abs(a[j]-a[i]));其中第一次用f[j]来更新f[j]就是用上一次的答案来更新当前的答案。

算法三
由于m比较小,根据抽屉原理,一旦区间长度大于m,那么一定存在两个点相等,那么答案就为0了。所以我们只需要考虑区间长度<=m的部分,时间复杂度 O(nm) 。再结合算法二加点什么滚动数组之类的优化,期望得分40分。

算法四
设区间的长度为x,那么最小值不超过 mx1
设一个常数 S
若x<=S,那么用算法三的方法,可以做到O(nS)
若x>S,那么最小差不会超过 mS1 。设 rx 表示权值为x的数最后出现的位置。每次新加入一个数 z 了之后,枚举差值|zy|,对于所有的 |zy|<=mS1 ,用 ry 来更新答案。然后每一次更新 rx 就可以了。
不过这里有很多需要注意的问题。首先是算区间的长度,区间中的元素个数当然是r-l+1,但是题目中所给的计算公式实际上是r-l。在算法四中,如果用f[i]来表示当前右端点为r时,左端点能到的最远点(不包括f[i]),那么最后计算答案的时候应该是i-f[i]-1,而且差值不应该是枚举的差值,而应该+1。

代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define LL long long #define N 200005 int n,m,k,S; int a[N],f[N],r[N]; LL ans; int Abs(int x) { return (x>0)?x:-x; } int main() { scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;++i) scanf("%d",&a[i]); S=sqrt(m); memset(f,127,sizeof(f)); for (int i=n;i>=1;--i) for (int j=i+1;j<=min(n,i+S-1);++j) { f[j]=min(f[j],f[j-1]); f[j]=min(f[j],Abs(a[j]-a[i])); if (j-i+1>=k) ans=max(ans,(LL)f[j]*(j-i)); } memset(f,0,sizeof(f)); for (int i=1;i<=n;++i) { for (int j=0;j<=m/S+1;++j) { if (j>=1) f[j]=max(f[j],f[j-1]); if (a[i]-j>=1) f[j]=max(f[j],r[a[i]-j]); if (a[i]+j<=m) f[j]=max(f[j],r[a[i]+j]); if (i-f[j]>=max(k,S)) ans=max(ans,(LL)(i-f[j]-1)*(j+1)); } r[a[i]]=i; } printf("%lldn",ans); }

总结

最后

以上就是雪白鸵鸟最近收集整理的关于[UOJ#246][UER#7C]套路(数luan学gao相关)的全部内容,更多相关[UOJ#246][UER#7C]套路(数luan学gao相关)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部