北校门外的回忆

这篇题解我真的是写了好多遍,每次快写好的时候就各种情况然后写的消失了

题目链接

这题要你求错误代码的结果,所以不是维护前缀异或和

这题暴力时间上是可以过的,但是空间不行

想到用动态开点线段树维护树状数组

这下时间过不去了

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
",ans);
		}
	}
} 
原文地址:https://www.cnblogs.com/Doingdong/p/10710203.html