P4135 作诗 题解
题目强制在线,不让你用莫队,你就不写了?你还真写分块啊?
这是一种非正解但可以过的做法,我命名为【并行莫队】。若要学习分块正解,请跳过本题解。
感谢 @little_grass_sage 赞助的看起来根本没啥用其实强得不得了的优化!
题意简述
给定
数据范围:
solution
先考虑如果不强制在线的做法:很显然可以用莫队算法解决。强制在线意味着我们不可以提前获取到所有的查询,只能按顺序处理,看上去莫队将退化到最坏
回顾莫队算法思想,如果我们知道一个区间
其实核心不在于“上一次”,而在于“这些区间我们有答案,以及维护答案的额外东西”。本题中“维护答案的额外东西”就是
在线用莫队解决的弊端在于我们只有一个区间答案以及维护答案的额外东西,如果不能离线查询,上一次和这一次的区间查询往往差距过大(变动次数过多,不可控),导致查询退化为
我们直接初始时随机选取左端点
看起来这个“算法”思维很容易理解,复杂度感觉也很优秀,那么具体的表现呢?很遗憾,我们得到了 TLE 80pts。
怎么优化呢?考虑前
这个优化看似有限,取
那么,具体的时间复杂度呢?如果要卡掉的话估计难度很大,所以先不要脸地假装数据随机。这意味着
这种算法由于开了很多个区间,这些是并行的参与到莫队,所以叫【并行莫队】。我个人的确想不到什么办法卡掉这种算法,但思想具有启发性,并且也过了,适宜我们这些不会分块的人民骗分,所以发出来,欢迎大家证明 / 证伪复杂度或者加 hack。
还有一点:为什么我们肯定这题数据随机?题目的加密强制在线方式如果想要特殊构造 hack 数据是非常简单的,如果想要特殊构造,当然也应该负责保证
好了,就这样吧,代码:
const int s=1000;
int n,c,m,a[100010],L[1005],R[1005],cnt[1005][100010],ans[1005],lastans;
int main(){
cin.tie(0)->sync_with_stdio(0);
cout.tie(0);
cin>>n>>c>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int _=1;_<=m;_++){
int l,r;
cin>>l>>r;
l=(l+lastans)%n+1;
r=(r+lastans)%n+1;
if(l>r)swap(l,r);
int c=1;
if(_<=s)c=_,L[_]=l,R[_]=L[_]-1;
else for(int i=1;i<=s;i++)if(abs(l-L[i])+abs(r-R[i])<abs(l-L[c])+abs(r-R[c]))c=i;
while(L[c]<l){
if(cnt[c][a[L[c]]]&&cnt[c][a[L[c]]]%2==0)ans[c]--;
cnt[c][a[L[c]]]--;
if(cnt[c][a[L[c]]]&&cnt[c][a[L[c]]]%2==0)ans[c]++;
L[c]++;
}
while(L[c]>l){
L[c]--;
if(cnt[c][a[L[c]]]&&cnt[c][a[L[c]]]%2==0)ans[c]--;
cnt[c][a[L[c]]]++;
if(cnt[c][a[L[c]]]&&cnt[c][a[L[c]]]%2==0)ans[c]++;
}
while(R[c]<r){
R[c]++;
if(cnt[c][a[R[c]]]&&cnt[c][a[R[c]]]%2==0)ans[c]--;
cnt[c][a[R[c]]]++;
if(cnt[c][a[R[c]]]&&cnt[c][a[R[c]]]%2==0)ans[c]++;
}
while(R[c]>r){
if(cnt[c][a[R[c]]]&&cnt[c][a[R[c]]]%2==0)ans[c]--;
cnt[c][a[R[c]]]--;
if(cnt[c][a[R[c]]]&&cnt[c][a[R[c]]]%2==0)ans[c]++;
R[c]--;
}
cout<<ans[c]<<'\n';
lastans=ans[c];
}
return 0;
}