XJTUOJ #1149 czq的模k异或
题目描述
给定一个长度为\(n\)初始全为0的数列,下标从1开始。定义操作模k异或v为对所有满足\(i\equiv 0\:(mod\:k)\)的下标\(i\),将\(a_i\)异或上整数\(v\)(即令\(a_i=a_i\bigoplus\:v\) )。
给出\(q\)次操作,每次操作之后输出序列的异或和,并且在操作结束之后输出整个序列。
序列的异或和为\(a_1\bigoplus a_2\bigoplus ...\bigoplus a_n\)
输入格式
第一行两个整数\(n,q\)。
接下来q行,每行两个整数\(k_i,v_i\)。
输出格式
输出共\(q+1\)行,其中前\(q\)行每行一个整数,为每次操作结束后的序列的异或和。
最后一行为操作结束后的序列。
数据范围
\(1\leq n,q\leq 2*10^5\)
\(0\leq k_i,v_i\leq 10^9\)
思路
可以发现有两个子问题,其中第一个:求每次操作完后数列的异或和,是简单的。
由\(x\bigoplus x=0\)可得,只要求出1-n中有多少个数是\(k_i\)的倍数,如果是奇数个,那么答案异或上\(v_i\),如果是偶数个,答案不变。
对于第二个子问题,可以想到,既然操作是位运算,那么应该把每一位拆开来处理,我们把初始数组a拆成31个0/1数组,那我们对0/1数组进行操作,就只需要考虑\(k_i\)
(因为异或\(v_i\)相当于对\(v_i\)每一位对应的0/1数组进行操作)。
考虑维护1个0/1数组,每次操作是对于\(k|i\),\(a[i]\bigoplus =1\),那么可以维护所有的k(相同的合并),对于每个个数为奇数的k扫一遍。可是这样最坏情况下不是\(n^2\)的时间复杂度吗?
其实考虑到相同的k会被合并,最坏情况是\(k=1,2,...n\),执行次数为\(\frac{n}{1}+\frac{n}{2}+...+\frac{n}{n}\)。不会超时。
然后把31个0/1数组的信息合并就是最后的答案数组。
代码
#include<cstdio>
#include<cstdlib>
#define maxn 200010
using namespace std;
int a[maxn],x,y,op[32][maxn];
int main(){
int i,j,n,m,sum=0,x,y,k,N=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i){
scanf("%d%d",&x,&y);
if(x>n){
printf("%d\n",sum);
continue;
}
if(x>N) N=x;
if((n/x)&1) sum^=y;
for(j=0;j<31&&(1<<j)<=y;++j){
if((1<<j)&y) op[j][x]^=1;
}
printf("%d\n",sum);
}
for(i=0;i<31;i++){
for(j=1;j<=N;j++){
if(!op[i][j]) continue;
for(k=j;k<=n;k+=j)
a[k]^=(1<<i);
}
}
for(i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}