LOJ510 [LibreOJ NOI Round #1] 北校门外的回忆

建立图论模型,\(x\)\(x+\operatorname{lowbit}(x)\) 连边。当 \(k\) 为奇数时,因为 \(k \perp 2\),所以 \(x\) 无论加多少次 \(\operatorname{lowbit}(x)\)\(\operatorname{lowbit}(x)\) 的位数不会改变,得形成的图为若干条链,链相互不交。当 \(k\) 为偶数时,当最低位的数字含有的质因数 \(2\) 的个数不小于 \(k\) 的个数时,其和奇数的情况等价,会形成若干不交的链,也就是 \(k\) 为偶数时得到的图为若干相交的链。

直接建图维护链的前缀异或和,复杂度无法接受,需要进一步优化。因为当最低位的数字中 \(2\) 的指数不小于 \(k\) 中的时,其就已经到了不交的链上,所以从任何一点,最多走 \(\log\) 次就能到不交的链上。到链之前的信息暴力维护,链上的信息用动态开点线段树维护即可。

现在的问题就只剩下走到一条不交的链上后,怎么知道其是哪一条链。可以在每一条链上选出一个代表点,其只有在最低位有值,每次到一条链上倍增跳到代表点,就能知道当前是哪一条链了。设 \(f_{i,j}\) 为最低位为 \(i\),将其除了最低位的高位减去 \(2^j\),会跳到哪里,处理出后就能倍增知道在哪一条链了。

#include<bits/stdc++.h>
#define maxn 200010
#define maxm 6000010
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,q,k,tot,all;
int f[maxn][32],sum[maxm],ls[maxm],rs[maxm];
map<int,int> val;
map<pair<int,int>,int> rt;
void modify(int l,int r,int pos,int v,int &cur)
{
    if(!cur) cur=++tot;
    sum[cur]^=v;
    if(l==r) return;
    if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
    else modify(mid+1,r,pos,v,rs[cur]);
}
int query(int l,int r,int pos,int cur)
{
    if(!cur||l==r) return sum[cur];
    if(pos<=mid) return query(l,mid,pos,ls[cur]);
    else return sum[ls[cur]]^query(mid+1,r,pos,rs[cur]);
}
int get(int x)
{
    while(!(x&1)) x>>=1;
    return x;
}
int find(int v)
{
    int x=get(v%k);
    v/=k;
    for(int i=0;(1<<i)<=v;++i)
        if(v&(1<<i))
            x=f[x][i];
    return x;
}
void init()
{
    int t=get(k);
    for(int i=1;i<t;i+=2) f[i][0]=get(i+t);
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i<t;i+=2)
            f[i][j]=f[f[i][j-1]][j-1];
    all=(k&(-k))-1;
}
int main()
{
    read(n),read(q),read(k),init();
    while(q--)
    {
        int opt,x,v,p=1,ans=0;
        read(opt),read(x);
        while(x%k==0) x/=k,p*=k;
        if(opt==1)
        {
            read(v);
            while(x*p<=n&&x&all)
            {
                val[x*p]^=v,x+=x%k;
                while(x%k==0) x/=k,p*=k;
            }
            if(x*p<=n) modify(1,n,x*p,v,rt[{find(x),p}]);
        }
        else
        {
            while(x)
            {
                if(x&all) ans^=val[x*p];
                else ans^=query(1,n,x*p,rt[{find(x),p}]);
                if(!(x-=x%k)) break;
                while(x%k==0) x/=k,p*=k;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
posted @ 2020-10-31 20:06  lhm_liu  阅读(290)  评论(0编辑  收藏  举报