XUANMING'S JOURNAL屏幕在深夜微微发亮,我心在远方。
← 回到笔记
FIELD NOTES

题解 P7096 【[yLOI2020] 泸沽寻梦】

这篇笔记的路线图

场外选手来水波题解。

Part.1

先考虑询问怎么做。

首先我们考虑异或有一个很好的性质,可差分性。

考虑对序列做一个前缀异或和,一段区间$[l,r]$的异或和为$0$,实际上就是$sum[r]\oplus sum[l-1]=0$。所以现在我们只用查询序列中有几个相等的数对,这个就可以随便做了。

Part.2

考虑修改操作很有特点,每次修改的数都是相邻的两个数,对前缀和的影响是$sum[p]$变为了$sum[p]\oplus x$,$sum[p+1...n]$变为了$sum[p+1...n]\oplus x\oplus x=sum[p+1...n]$,实际上发生改变的前缀就只有$sum[p]$!

所以问题就变得很简单了,我们只需要先扫一遍统计一下开始的答案,每次先删除原来$sum[p]$的贡献,再加入修改后的$sum[p]$就可以了。

随便拿个值域数据结构做就是了,我写了个$01\text{trie}$,当然拿动态开点值域线段树或平衡树,$\text{map}$之类的都可以啦。

答案记得开$\text{long long}$。

代码:

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1e6+5,lim=29;

int tr[maxn*lim][2],siz[maxn*lim],sum[maxn],cnt=1,n,m,cntx;
ll totans,mx,mi;

template<typename T>
inline void read(T &x)
{
    int f=1;char c;
    while(!isdigit(c=getchar())) if(c=='-') f=-1;
    x=c^48;
    while(isdigit(c=getchar())) x=x*10+(c^48);
    x*=f;
}

void insert(int x,int f=1)
{
    int u=1;
    for(int i=lim;~i;--i)
    {
        int c=x>>i&1;
        if(!tr[u][c]) tr[u][c]=++cnt;
        u=tr[u][c];siz[u]+=f;
    }
}

int query(int x)
{
    int u=1;
    for(int i=lim;~i;--i)
    {
        int c=x>>i&1;
        u=tr[u][c];
        if(!u) return 0;
    }
    return siz[u];
}

int main()
{
    int p,x;
    ll ans=0;
    read(n);read(m);
    for(int i=1;i<=n;++i)
        read(sum[i]),sum[i]^=sum[i-1];
    for(int i=0;i<=n;++i)
        ans+=query(sum[i]),insert(sum[i]);
    mx=0;mi=1e18;
    while(m--)
    {
        read(p);read(x);
        insert(sum[p],-1);
        ans-=query(sum[p]);
        sum[p]^=x;
        ans+=query(sum[p]);
        insert(sum[p]);
        totans^=ans;cntx+=(ans&1);
        mx=max(mx,ans);mi=min(mi,ans);
    }
    printf("%lld\n%d\n%lld\n%lld\n",totans,cntx,mx,mi);
    return 0,QAQ;
}

输入关键词,搜索全部笔记。