作诗
首先将原区间分块(设块的大小是T)
先处理处每一个数字的vector,蒲公英那题的vector一样
然后处理出
具体见代码,这里主要讲一下时间复杂度
时间复杂度不是
对于每一个询问
如果询问的端点再同一个块里面,暴力循环记录每一个数字的出现次数(cnt数组)输出答案,然后再暴力循环把每一个数字对cnt数组的改变给变回来。复杂度
如果不在同一块里面,设左端点的块为
首先让ans加上
对需要暴力处理的每一个数字,统计出来它在暴力区间里面的个数和整个区间的个数,分别为a和b,那么
若
若
复杂度为
所以取
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,c,m;
int a[N];
int L[N],R[N];
int f[4000][4000],belong[N],Count[N];
bool num[N],mark[N];
vector<int> pos[N];
int getnum(int x,int y,int k)
{
if(x>y) return 0;
int l=0,r=pos[k].size()-1,mid,res1,res2;
//这里其实是有可能会出错的
//如果某一时刻l=r=0,就死循环了
//但就此题数据而言不会
//然而以后二分时还是要注意尽量区间别弄到0
if(x>pos[k][r]||y<pos[k][0]) return 0;
while(l<=r)
{
mid=l+r>>1;
if(pos[k][mid]>=x)
{
res1=mid;
r=mid-1;
}
else l=mid+1;
}
l=0,r=pos[k].size()-1;
while(l<=r)
{
mid=l+r>>1;
if(pos[k][mid]<=y)
{
res2=mid;
l=mid+1;
}
else r=mid-1;
}
return max(res2-res1+1,0);//一定要注意res2小于res1的情况
}
int main()
{
scanf("%d%d%d",&n,&c,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]].push_back(i);
}
int t=pow((ll)n*n/(2*m),1.0/3),cnt=n/t;
//注意pow函数的用法
//pow的两个参数都是double,可以这么用
for(int i=1;i<=cnt;i++)
{
L[i]=(i-1)*t+1;
R[i]=i*t;
}
if(R[cnt]<n)
{
cnt++;
L[cnt]=R[cnt-1]+1;
R[cnt]=n;
}
for(int i=1;i<=cnt;i++)
{
//for(int j=1;j<=c;j++) Num[i][j]=Num[i-1][j];
//上面被注释的代码是用前缀和优化的代码
//但是由于acwing的内存限制得太死
//导致只能用蓝书上的二分
//复杂度多了一个块长
//必须要选取合适的块长
for(int j=L[i];j<=R[i];j++)
{
//Num[i][a[j]]++;
belong[j]=i;
}
}
for(int i=1;i<=cnt;i++)
{
int res=0,tot=0;
//res表示目前为止从i到j出现了奇数次的数字的个数
//tot表示目前为止从i到j一个出现了多少种数字
for(int j=i;j<=cnt;j++)
{
for(int k=L[j];k<=R[j];k++)
{
if(num[a[k]]^1) res++;
else res--;
num[a[k]]^=1;
if(!mark[a[k]])
{
tot++;
mark[a[k]]=1;
}
}
f[i][j]=tot-res;
}
for(int j=i;j<=cnt;j++)
for(int k=L[j];k<=R[j];k++) num[a[k]]=mark[a[k]]=0;
//注意这种还原技巧学会
//没必要上memset
}
int ans=0;
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l+ans)%n+1,r=(r+ans)%n+1;
if(l>r) swap(l,r);
if(belong[l]==belong[r])
{
for(int j=l;j<=r;j++)
Count[a[j]]++;
int res=0;
for(int j=l;j<=r;j++)
if((Count[a[j]]&1)==0&&!mark[a[j]])
{
mark[a[j]]=1;
res++;
}
printf("%d\n",res);
ans=res;
for(int j=l;j<=r;j++)
{
Count[a[j]]--;
mark[a[j]]=0;
}
}
else
{
int p=belong[l],q=belong[r];
int res=0;
if(p!=q-1) res+=f[p+1][q-1];
int x=0,y=0;
for(int j=l;j<=R[p];j++)
if(!mark[a[j]])
{
//下面如果有前缀和优化
//就可以少一个循环
//块长就可以取根号n
mark[a[j]]=1;
for(int k=j;k<=R[p];k++)
if(a[k]==a[j]) x++;
for(int k=L[q];k<=r;k++)
if(a[k]==a[j]) x++;
y=getnum(L[p+1],R[q-1],a[j]);//蓝书的做法
if((y&1)&&(x&1)) res++;
else if(y&&(!(y&1))&&(x&1)) res--;
else if(!y&&(!(x&1))) res++;//一定要考虑这个数字在整块中一次都没出现的情况
x=y=0;
}
for(int j=L[q];j<=r;j++)
if(!mark[a[j]])
{
mark[a[j]]=1;
for(int k=j;k<=r;k++)
if(a[k]==a[j]) x++;
y=getnum(L[p+1],R[q-1],a[j]);
if((y&1)&&(x&1)) res++;
else if(y&&(!(y&1))&&(x&1)) res--;
else if(!y&&(!(x&1))) res++;
x=y=0;
}
printf("%d\n",res);
ans=res;
for(int j=l;j<=R[p];j++) mark[a[j]]=0;
for(int k=L[q];k<=r;k++) mark[a[k]]=0;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构