题目大意
现在你要构造一个长度为n,字典序最小的数组a。
给定m个限定要求,每个要求形如(l,r,x),可能合法也可能不合法。表示要数组满足a[l]^…a[r]=x,如果满足之前的合法要求之后无法满足这个要求,则这个要求不合法,否则合法。强制在线,每次读入一个要求后,合法输出1,否则输出0。
n<=200000, m<=400000
分析
这其实远古时代做过…比赛的时候不会做,吃了记忆力不好的亏。
先不考虑怎么让a字典序最小,先考虑怎么判断要求是否合法。
很关键的一点是把要求化成前缀xor和的形式,设sum[i]=sum[i-1]^a[i],则限制变为sum[r]^sum[l-1]==x。这样相当于把区间限制变成了两点限制。那么一个合法限制相当于在两个点a,b间连一条边,边权为x,代表sum[a]^sum[b]==x。然后不合法的限制就是和之前形成的a到b的路径xor和矛盾。
一个经典做法是带权并查集,这是他的一个应用。考虑到xor的优美性质,我们可以路径压缩。对于a,b在同一个并查集的情况,是不用再连边的,简单地判断并查集路径xor和是否满足限制即可;对于不同并查集,把父亲连起来即可,边权计算一下可以弄出来,相当于原来一些路径的xor和。(就不用连a,b了)。
代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
typedef long long ll;
typedef double db;
const int N=2e5+5;
int val[N],fa[N],sum[N],l,r,z,i,n,m,s,lst,pp,x,y,pd[N];
int get(int x)
{
if (fa[x]==x) return x;
int ret=get(fa[x]);
val[x]^=val[fa[x]];
return fa[x]=ret;
}
int main()
{
freopen("t1.in","r",stdin);
freopen("t1.out","w",stdout);
scanf("%d %d %d",&n,&m,&s);
fo(i,0,n) fa[i]=i;
fo(i,1,m)
{
scanf("%d %d %d",&l,&r,&z);
l^=s*lst;r^=s*lst;z^=s*lst;
x=get(l-1);
y=get(r);
pp=1;
if (x!=y)
{
val[x]=val[l-1]^val[r]^z;
fa[x]=y;
}else if ((val[r]^val[l-1])!=z) pp=0;
lst=pp;
printf("%d\n",pp);
}
fo(i,0,n)
if (!pd[i])
{
x=get(i);
if (!pd[x])
{
sum[i]=sum[i-1];
sum[x]=sum[i]^val[i];
pd[x]=1;
}else
{
sum[i]=sum[x]^val[i];
}
pd[i]=1;
}
fo(i,1,n) printf("%d\n",sum[i]^sum[i-1]);
}