北校门外的回忆

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

题目链接

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

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

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

这下时间过不去了

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);
        }
    }
} 

转载于:https://www.cnblogs.com/Doingdong/p/10710203.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值