值得纪念。
但以后不会再有了QAQ
A
其它的增高相当于自己降低。所以按顺序输出(1)至(n)即可。
(由于一开始看反了导致挂了一次……)
B
两点间随意找一条路径操作,路径中间的点没有影响,只会影响端点。
全局找任意对数将其符号取反。贪心即可。
C
先从大到小排序,能选就选。由于要求的下界是上界的一半,从大往小选,不存在少选一个大的,多选几个小的才能满足的情况。
(似乎不排序也能做)
D
朴素DP。直接设(f_{i,j}),也就是普通的LCS问题改点细节而已。
E
D到E花了1.5h,E到F1花了不到0.5h。我在干嘛……
https://www.cnblogs.com/jz-597/p/13986661.html
F
显然全局众数也是最优区间的众数之一(否则考虑最优区间扩张的过程,必定存在一次局部众数和全局众数的出现次数相等)。
找出全局众数,分别枚举每一个数,变成如下问题:全局众数出现的位置为(+1),这个数出现的位置为(-1),其它位置为(0),求最长的和为(0)的区间。
第一档部分分直接开个桶来维护。时间(O(100n))。贴代码:
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200005
int n,m;
int a[N];
int s[N][101];
int p[N];
int buc[N*2];
void upd(int &a,int b){
if (a==-1) a=b;
}
int main(){
// freopen("in.txt","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
memcpy(s[i],s[i-1],sizeof s[i]);
s[i][a[i]]++;
}
int id=1,mx=s[n][1];
for (int i=2;i<=100;++i)
if (s[n][i]>mx)
mx=s[n][i],id=i;
else if (s[n][i]==mx)
id=-1;
if (id==-1){
printf("%d
",n);
return 0;
}
p[0]=0;
for (int i=1;i<=n;++i)
if (a[i]==id)
p[++m]=i;
p[++m]=n+1;
memcpy(s[n+1],s[n],sizeof s[n]);
int ans=0;
for (int i=1;i<=100;++i){
if (i==id)
continue;
memset(buc,255,sizeof buc);
for (int j=0;j<=m;++j){
// if (j==3)
// j=3;
// printf("query(%d)
",s[p[j]][i]-j);
if (buc[s[p[j]][i]-j +N]!=-1)
ans=max(ans,p[j]-buc[s[p[j]][i]-j +N]-1);
// printf("upd(%d,%d)
",s[p[j]][i]-j-1,p[j]);
upd(buc[s[p[j]][i]-j-1 +N],p[j]);
}
}
printf("%d
",ans);
return 0;
}
对于第二档,题解做法:如果这个数的出现次数(|V|>sqrt n),那么用上述方法(O(n))做;否则,对于这个数的每个出现位置(V_i),提取出左右离它最近的全局众数,一共要做(O(|V|))个位置,每个位置要么是一个(-1)或一段(+1)。可以直接(O(|V|^2))。总时间(O(nsqrt n))。
其实也可以做到(O(nlg n))。用线段树维护区间的前缀最大值和最小值。当需要找一个值的的第一次出现位置的时候,由于每次只会(+1)或(-1),是连续的,如果这个值在最小值最大值之间,就一定存在。那么在线段树上二分即可。
都没写。
感觉这次发挥比较好。
原因大概有:
- 这次咖啡选择在晚餐后喝,比赛期间精神饱满,肾上腺素激增,上厕所时能感受到心跳,打题时感觉到右臂的动脉在动。(我不是在吹)。
- 这次题目比较适合我发挥,不像上次div2那样全场构造。
- 2:30的写题时间,第一次div2切了超过4题。
- 秒切了不知道为什么没什么人写的F1。
当然了这次也存在一些败笔。在想题的时候(尤其是E题),绕了很长的弯子,中间想了个可能不靠谱的阴间做法,交了一遍自然挂了。感觉应该要更严谨一些,那种做法应该被ban掉的。
div1我来啦!