带修改线性基
最近模拟赛中有带修线性基这个我 (闻所未闻 见所未见) 的黑科技。
我们仍未知道那天所见到的黑科技的名字
这道例题是动态加边 每次询问一个独立集的最大权值。这里的权值定义为题目中的定义。
对于前两个测试点 暴力枚举选出了哪些点 然后判断哪些边可以用 通过这个暴力的操作可以发现 将边的权值异或到点上 最后用点进行异或即可将那些不能使用的边给异或掉。
于是可以发现这是一个选出若干个点使异或和最大的题目->线性基的题目。
观察3,4,5测试点 无自环呈若干个联通块状 每个连通块最多只有一条边 点权不带修改 动态加入权值的线性基的题目。
剩下的测试点其实是 每次有两个点的权值变化 在线维护线性基。
对于这种问题 一种比较粗暴的方法 是直接上线段树分治维护线性基 记录一下每次权值插入的线性基的位置然后方便撤销。
复杂度\(mlog^2\cdot \frac{l}{w}\).(这个复杂度可能可以过 我也没试过。
黑科技 在线维护带修改线性基。
具体操作:
维护线性基的同时 再维护每线性基中每一个主元是由哪写向量异或而成的。
修改其中的一个向量x的时候 找到由它异或而成的行中1的位数最低的那个行向量w(有零行选零行.
找到之后将其他被x异或过的向量 再一一异或一遍 因为此时我们把w当作x 此时w要被修改所以凡是异或过x的都要修改。
之所以选最小的是因为修改其他行向量的时候不会改变线性基的性质。
考虑一个向量原本不在线性基中被我们修改过后又可以存在于线性基中这种情况不存在 因为村子啊这种情况这个向量一定是零行 被优先选择的 所以优先选择了零行那么显然 这种情况亦不存在。
我们再将w异或当前要异或的值 再插入线性基中即可。线性基的性质依旧保留 。复杂度\(\frac{nml}{w}\)
const int MAXN=510,maxn=1010;
int n,m,maxx;
int s[maxn];
bitset<maxn>b[MAXN],c[MAXN],w,ans;
char a[maxn];
inline void add(int x)
{
fep(maxx,0,i)
{
if(b[x][i])
{
if(!s[i]){s[i]=x;break;}
else
{
b[x]=b[x]^b[s[i]];
c[x]=c[x]^c[s[i]];
if(!b[x].any())return;
}
}
}
}
inline void modify(int x)//寻找最小的被x xor的主元
{
int ww=0;
for(int i=1;i<=n;++i)if(c[i][x]&&!b[i].any()){ww=i;break;}
if(!ww)rep(0,maxx,i)if(c[s[i]][x]&&s[i]){ww=s[i];s[i]=0;break;}
rep(1,n,i)
if(c[i][x]&&i!=ww)
{
b[i]=b[i]^b[ww];
c[i]=c[i]^c[ww];
}
b[ww]^=w;add(ww);
}
int main()
{
freopen("cut.in","r",stdin);
freopen("cut.out","w",stdout);
gt(n);gt(m);
rep(1,n,i)c[i][i]=1;
rep(1,m,i)
{
int x,y;
gt(x);gt(y);
scanf("%s",a);
int len=strlen(a);--len;
maxx=max(maxx,len);
w.reset();
fep(len,0,j)w[j]=a[len-j]-'0';
modify(x);
modify(y);
int mark=0;
ans.reset();
fep(maxx,0,j)
{
if(!ans[j]&&s[j])ans^=b[s[j]];
if(ans[j]||mark)
{
mark=1;
printf("%d",ans[j]?1:0);
}
}
if(!mark)printf("0");
puts("");
}
return 0;
}
不过这个带修是指异或的修改 如果是直接修改成某个值x呢?
可以发现我们转成异或修改即可。