题解 P4062 【[Code+#1]Yazid 的新生舞会】
蒟蒻提供一种
先考虑部分分怎么做。
对于
变一下柿子为:
枚举
不过为了后面的根号算法能过,还得优化一下这个计算过程,想办法丢掉一个
考虑到
怎么修改?
这样一来查询的区间只会往左或往右移一位,那么就得知道多出来的一位或少了的一位贡献是多少,得到对应的变化量,开个桶记录一下所有的
这样就能做到
对于
对于
接下来就是根号算法啦!
上面的两种做法区别在于一个是针对数字种类,一个是针对数字出现次数。
不妨对每个数字出现的出现次数分个类。
对于所有
对于
如果你不想用
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL;
int read() {
char ch=getchar();int x=0,pd=0;
while(ch<'0'||ch>'9') pd|=ch=='-',ch=getchar();
while('0'<=ch&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return pd?-x:x;
}
const int maxn=505005;
int _max(int a,int b) { return a<b?b:a; }
int _min(int a,int b) { return a<b?a:b; }
int n,type;
int a[maxn];
int cnt[maxn],vis[maxn],cnt2[maxn];
int s1[maxn];
LL ans;
int main() {
n=read(),type=read();
for(int i=1;i<=n;i++) ++vis[a[i]=read()];
int size=sqrt(n),limit=size*2;
for(int i=1;i<=n;i++) {
int maxx=0;
for(int j=i;j<i+limit&&j<=n;j++) {
if(vis[a[j]]<=size) maxx=_max(maxx,++cnt[a[j]]);
if(maxx>(j-i+1)/2) ++ans;
}
for(int j=i;j<i+limit&&j<=n;j++)
if(vis[a[j]]<=size) --cnt[a[j]];
}
for(int num=0;num<n;num++)
if(vis[num]>size) {
for(int i=0;i<=n+15;i++) cnt2[i]=0;
int minn=0x3f3f3f3f;
for(int i=1;i<=n;i++) {
s1[i]=s1[i-1]+(a[i]==num);
minn=_min(minn,2*s1[i]-i);
}
int p=1-minn+1,sum=0;
cnt2[p]=1; // 有负数,加个偏移量
for(int i=1;i<=n;i++) {
if(s1[i]==s1[i-1]) sum-=cnt2[ 2*s1[i-1]-(i-1)-1+p ];
else sum+=cnt2[ 2*s1[i-1]-(i-1)+p ];
++cnt2[2*s1[i]-i+p];
ans+=sum;
}
}
printf("%lld\n",ans);
return 0;
}