5.2 省选模拟赛 或许 线型基
LINK:或许
考试的时候 失了智 完全没想到这道题的做法。
以为很难 原因是没有认真分析 (当时状态确实不好
只草草的打了暴力 包括暴力只能跑到14分 发现很多人A掉了这道题就发现隐隐不对。
考虑 此时能用的数字为 s1,s2,s3...
对于一个数字x 那么此时 x^s1 和 x是联通的 容易发现 xs1s2也和x是联通的。
不断拓展下去 容易发现x能异或出来的数字和s所形成的线型基的大小有关。
且最终答案为 \(2^{n-sz}\) 其中sz为线型基的大小。
那么题目就是 每次加入一个数字x 删除一个数字x 维护线型基的大小。
解决这个问题的经典做法是线段树分治。
不过两个log过不了。
考虑在线维护线型基 容易想到每次如果删除的话 以前可能加不进去的x可能可以加进去了。
为了防止出现这种情况 每次在尝试加入线型基的时候 维护一个时间最大的线型基。
这样可以保证 加入x之后 之前没有可以加进去的x了。所以加入的操作为 遇到能影响当前x的 比一下时间 尽量使得时间长的在主元的位置上。
考虑删除 直接找到当前的时间要被删除的x删掉即可。
可以发现如果存在x影响其他的点还在线型基中 显然不可能 因为这些点必然比x小删除时间比x要大 如果出现这种情况显然两者会被交换。
综上删除是合法的 删除x 不会出现之前阐述的情况 也不会存在x影响的点。使用map会被卡常 sort比map快多了。
const int MAXN=2000010,N=40;
int n,Q;
int w[MAXN],a[MAXN];
int f[N],c[N],sz;
struct wy{int x,id;}t[MAXN];
inline int cmp(wy a,wy b){return a.x==b.x?a.id<b.id:a.x<b.x;}
inline void insert(int x,int w)
{
fep(n-1,0,i)
{
if(x&(1<<i))
{
if(!f[i])
{
f[i]=x;
c[i]=w;
++sz;return;
}
else
{
if(c[i]<w)swap(x,f[i]),swap(w,c[i]);
x=x^f[i];
}
}
}
}
inline void del(int x)
{
rep(0,n-1,i)if(c[i]==x){f[i]=c[i]=0;--sz;return;}
}
int main()
{
freopen("1.in","r",stdin);
get(n);get(Q);
rep(1,Q,i)
{
int op,x;
get(op);get(x);
t[i]=(wy){x,i};
if(op==1)a[i]=x;
}
sort(t+1,t+1+Q,cmp);
rep(1,Q,i)
{
for(int j=0;;++j)
{
if(t[j+i].x!=t[i].x)
{
i=i+j-1;
break;
}
if(!(j&1))
{
if(t[i+j+1].x==t[i+j].x)w[t[i+j].id]=t[i+j+1].id;
else w[t[i+j].id]=Q+1;
}
}
}
int ans=0;
rep(1,Q,i)
{
if(a[i])insert(a[i],w[i]);
else del(i);
ans=ans^(1<<(n-sz));
}
put(ans);
return 0;
}