根据定义,若选择了一条边的一个端点,这条边就要选上,若这条边的两个点都选上了,这条边就不能选。
将边权异或到点权上,这样处理后,若选择一个点,则连接这个点的边都会被考虑,若一条边的两个端点都被选中,这条边会因为被异或两次而消失。
这样问题转化为每次加入一条边,对原图求线性基。
线性基不支持删除修改,由于位数有1000位,如果每加入一条边都暴力重新构造线性基,复杂度为
O
(
500
∗
1000
∗
1000
∗
30
)
O(500 * 1000 * 1000 * 30)
O(500∗1000∗1000∗30),30是使用 bitset 进行异或运算的常数,1000位异或 相当于进行30次 int 型异或运算。
来看看如何优化:
先观察问题的性质:暴力构造线性基时,由于每一次加入一条边都重新将所有点加入到线性基中,而每插入一条边,只会引起两个点权改变,M条边最多改变 2M个点的点权。在点 p 的两次改变的时间间隔之间,这个 p 点 只需要插入线性基一次。
考虑用线段树分治,按时间分治,线段树的每个节点维护这个时间段权值不会改变的点,在计算线性基时这些点只需要插入一次即可。而 dfs 时子节点的线性基又可以继承父节点的线性基,大大减少了修改时线性基的插入次数,当dfs 到叶子节点时,就得到了这个时间点的线性基。
计算一下这样处理的复杂度:M 次 加边至多修改 2M 个点的点权,每一次修改要在线段树上更新,复杂度为上限为 2 M log N 2M\log N 2MlogN。遍历线段树计算线性基时,每一个被修改的点会插入 log 次,因此总共插入 2 M log N 2M\log N 2MlogN 次,每一次插入的复杂度上限为为 O ( 1000 ∗ 30 ) O(1000 * 30) O(1000∗30)
因此复杂度上限大约为 O ( 1000 ∗ 30 ∗ 2 M log N ) O(1000 * 30 * 2M \log N) O(1000∗30∗2MlogN),上限时间大概 3 ∗ 1 0 8 3 * 10^8 3∗108,实际上这个上限跑不满。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
typedef bitset<1010> bit;
int n,m,lst[maxn];
char s[maxn];
bit val[maxn];
bit ans[maxn];
void print(bit x) {
int p = -1;
for(int i = 1000; i >= 0; i--) {
if(x[i] == 1) {
p = i;
break;
}
}
if(p == -1) putchar('0');
else {
for(int i = p; i >= 0; i--)
putchar(x[i] + '0');
}
putchar('\n');
}
struct line_basis{
bit a[maxn]; //线性基向量
void init() {
for(int i = 0; i <= 1000; i++)
a[i].reset();
}
void add(bit x) {
for(int i = 1000; i >= 0; i--) {
if(x[i] == 1) {
if(a[i].any()) x ^= a[i];
else {
a[i] = x;
break;
}
}
}
}
bit getval() {
bit t;
t.reset();
for(int i = 1000; i >= 0; i--)
if(!t[i] && a[i].any())
t ^= a[i];
return t;
}
};
struct seg_tree{
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
vector<bit> pot[maxn << 2];
void build(int rt,int l,int r) {
pot[rt].clear();
if(l == r) return;
int mid = l + r >> 1;
build(lson); build(rson);
}
void insert(int L,int R,bit v,int rt,int l,int r) {
if(L <= l && r <= R) {
pot[rt].push_back(v);
return ;
}
int mid = l + r >> 1;
if(L <= mid) insert(L,R,v,lson);
if(mid + 1 <= R) insert(L,R,v,rson);
}
void solve(int rt,int l,int r,line_basis t) {
for(auto it : pot[rt])
t.add(it);
if(l == r) {
ans[l] = t.getval();
return ;
}
int mid = l + r >> 1;
solve(lson,t); solve(rson,t);
}
}seg;
int main() {
scanf("%*d%d%d",&n,&m);
for(int i = 1; i <= n; i++)
val[i].reset(); //每一位清0
for(int i = 1; i <= m; i++)
ans[i].reset();
seg.build(1,1,m);
for(int i = 1,u,v; i <= m; i++) {
scanf("%d%d%s",&u,&v,&s);
int len = strlen(s);
reverse(s,s + len);
bit w; w.reset();
for(int j = 0; j < len; j++)
w.set(j,s[j] - '0');
if(lst[u])
seg.insert(lst[u],i - 1,val[u],1,1,m);
val[u] ^= w;
lst[u] = i;
if(lst[v])
seg.insert(lst[v],i - 1,val[v],1,1,m);
val[v] ^= w;
lst[v] = i;
}
for(int i = 1; i <= n; i++)
if(lst[i])
seg.insert(lst[i],m,val[i],1,1,m);
line_basis t;
t.init();
seg.solve(1,1,m,t);
for(int i = 1; i <= m; i++)
print(ans[i]);
return 0;
}