题解 P3770 【[CTSC2017]密钥】
题目
一个显然的暴力就是枚举
我们对第一个空位置做一遍这个暴力,考虑一下
如果移动到的位置原来是一个
移动到的位置是一个
于是我们维护一个桶,开一个指针表示当前
这样第一二问就做完了,第三问显然可以在用上述的方法模拟一遍,但是有这样一个结论,一个
证明非常简单,当一个前缀和从
于是对于第三问,我们只需要判断一下当前前缀和大于
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
const int maxn=2e7+5;
int p[maxn],pre[maxn],tax[maxn],np[maxn],id[maxn];
int seed,n,k,S,tot,now,ans1,ans2,beg,ans3;
inline int getrand() {
seed=((seed*12321)^9999)%32768;
return seed;
}
void generateData() {
scanf("%d%d%d",&k,&seed,&S);
int t=0;n=k*2+1;
for( int i = 1; i <= n; ++i )
p[i]=(getrand()/128)%2,t+=p[i];
int i=1;
while(t>k) {
while(p[i]==0) ++i;
p[i]=0;--t;
}
while(t<k) {
while(p[i]==1) ++i;
p[i]=1;++t;
}
}
inline void calc(int pos) {
if(tot==0) ans1=id[pos];
if(tot==S) ans2=id[pos];
if(tot==k-S) ans3=id[pos];
if(ans1&&ans2&&ans3) {
printf("%d\n%d\n%d\n",ans1,ans2,ans3);
exit(0);
}
}
int main() {
generateData();
for(re int i=1;i<=n;i++) if(!p[i]) {beg=i;break;}
for(re int i=1;i<=n;i++) {
id[i]=beg;np[i]=p[beg++];if(beg>n) beg=1;
}
for(re int i=2;i<=n;i++) pre[i]=(pre[i-1])+(np[i]?1:-1);
for(re int i=2;i<=n;i++) if(np[i]) tax[pre[i]+k]++;
for(re int i=k+1;i<=k+k;++i) tot+=tax[i];
calc(1);now=k;
for(re int i=2;i<=n;i++)
if(np[i]) ++now,tot-=tax[now],
tax[pre[i]+k]--,tax[pre[i]+k-1]++;
else tot+=tax[now],--now,calc(i);
return 0;
}