北校门外的回忆
这篇题解我真的是写了好多遍,每次快写好的时候就各种情况然后写的消失了
这题要你求错误代码的结果,所以不是维护前缀异或和
这题暴力时间上是可以过的,但是空间不行
想到用动态开点线段树维护树状数组
这下时间过不去了
x+lowbit(x)相当于x的最低位*2,x向x+lowbit(x)连边
在x不断向x+lowbit(x)跳的时候,lowbit(x)的非零位数字不断*2 mod k
当k是奇数时,2与k互质,所以如果2a=2b(mod k),a=b(mod k)
所以x -> x+lowbit(x)会形成若干不相交的链
当k是偶数时,链会相交,就处理出不相交的链,用动态开点线段树维护
对于不在链上的点就暴力跳到链上然后区间更新,在跳不超过log_2(k)∗log_k(n)=log_2(n)次后一定会跳到一条链上或跳出去。
在链上的点就求出其所在的链和它在链中的位置,然后区间更新、
lowbit(x)如果末尾有零,那x所在的链一定与模掉0之后的链是相似的,只要处理完后再把0乘回来即可
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;++i)
typedef long long ll;
typedef double db;
char cch;
inline int rd(){
int x=0,fl=1;
cch=getchar();
while(cch>'9'||cch<'0'){
if(cch=='-') fl=-1;
cch=getchar();
}
while(cch>='0'&&cch<='9') x=(x<<3)+(x<<1)+cch-'0',cch=getchar();
return x*fl;
}
const int N=2e5+5;
int st,dis,n,k,sz,pw[N],bz1[N][20],bz2[N][20],cnt[N],len[N],a[N];
bool vis[N];
struct tree{
int s,l,r;
}tr[N*80];
inline int lowbit(int x){//最低非零位上的数字
while(x%k==0) x/=k;
return x%k;
}
inline int lowbitv(int x){//最低非零位的值
int lg=1;
while(x%k==0) x/=k,lg*=k;
return x%k*lg;
}
inline void getst(int x){
int lg=1;
while(x%k==0) x/=k,lg*=k;//零是不影响的
st=lowbit(x);//在哪条链
int u=(x-st)/k;//要进几次位
dis=u/cnt[st]*len[st]+1;//往回跳了多少个点
u%=cnt[st];//剩下的不足一个环的 就倍增往回跳
for(int i=18;i>=0;i--) if(bz2[st][i]<=u) u-=bz2[st][i],dis+=(1<<i),st=bz1[st][i];
st*=lg;
}
void init(){
int x,tot,w,to;
rep(i,1,k){
x=i;
while(x%2==0) ++pw[i],x/=2;
}
for(int i=1;i<k;++i) if(!vis[i]&&pw[i]>=pw[k]){
tot=0,w=0,a[++tot]=i,x=(i<<1)%k;
while(!vis[x])
vis[x]=1,a[++tot]=x,x=(x<<1)%k;//a存的是lowbit值形成的一个环
rep(j,1,tot) w+=(a[j]<<1)>=k;//注意是往回跳,所以 to存的是 a[(j-1+tot)%tot]
rep(j,1,tot) cnt[a[j]]=w,len[a[j]]=tot,to=a[(j-1+tot)%tot],bz1[a[j]][0]=to,bz2[a[j]][0]=(to<<1)/k;//len是这个环点长度,cnt是完整跳一个环会进多少位
rep(h,1,18) rep(j,1,tot) bz1[a[j]][h]=bz1[bz1[a[j]][h-1]][h-1],bz2[a[j]][h]=bz2[bz1[a[j]][h-1]][h-1]+bz2[a[j]][h-1];//每个a[i]都更新,都作为链首
}//bz是往回跳会跳到哪个值的倍增数组,bz2是往回跳多少步会有多少进位的倍增数组
}
void ins(int &d,int l,int r,int ql,int qr,int z){
if (!d) d=++sz;
if (ql<=l&&r<=qr){tr[d].s^=z;return;}
int mid=(l+r)/2;
if (ql<=mid) ins(tr[d].l,l,mid,ql,qr,z);
if (qr>mid) ins(tr[d].r,mid+1,r,ql,qr,z);
}
int qry(int d,int l,int r,int x){
if(l==r||!d) return tr[d].s;
int mid=(l+r)/2;
if(x<=mid) return qry(tr[d].l,l,mid,x)^tr[d].s;
else return qry(tr[d].r,mid+1,r,x)^tr[d].s;
}
int main(){
n=rd();int ans,q=rd(),x,tmp,val,op,rt,root=0;k=rd();
init();
while(q--){
op=rd();
if(op==1){
x=rd(),val=rd();
while (x<=n&&pw[lowbit(x)]<pw[k]){//不在链上的点
ins(root,1,n,x,x,val);//单点修改
x+=lowbitv(x);
}
if(x>n) continue;//跳出n了
getst(x);//st是链首,dis是x在链中的位置
rt=qry(root,1,n,st),/*查找st所在链对应的线段树的根*/tmp=rt,ins(rt,1,n,dis,n,val);
if(!tmp)ins(root,1,n,st,st,rt);//root是维护rt的,单点查询,这样写可以省一些代码
}
else{
x=rd(),ans=0;
while(x){
if (pw[lowbit(x)]<pw[k]) ans^=qry(root,1,n,x);//对于那些不在链上的点,root维护的就是点的值
else{
getst(x);
int rt=qry(root,1,n,st);//因为x与x+lowbit(x)在同一条链,但是x与x+lowbit(x)-lowbit(x+lowbit(x))不一定在同一条链
if(rt) ans^=qry(rt,1,n,dis);//所以单点查询
}
x-=lowbitv(x);
}
printf("%d\n",ans);
}
}
}