题意:
给出一颗树,树的每条边上有权值(0或1)。
有两种操作:
1. 修改某个边的权值,0变为1,1变为0。
2. 查询当前的树有多少个不同的路径,使得路径上的权值异或和为1。
注意:起点和终点不同,算作不同一条路径。
思路:
其实发现这题是路径上的异或,那么就有更简单的做法了,因为不用真的求出所有的路径,确定一个根,那么两个点路径的权值异或和可以用它们到跟的路径的权值异或和表示。
例如:u -> v,可以分解为,节点1 -> u,节点1 -> v。
根据异或的性质,
u−>v的异或和 <==> 节点1−>u的异或和oxr节点1−>v的异或和。这样,只需要维护每个边到跟的路径的权值的异或和就行了。dfs一下,把树形结构转为线性结构,用线段树维护,然后就可以做了。
那么最后和异或和为1的路径,就是
ans=(从跟节点开始异或和为0的路径的条数)∗(从跟节点开始异或和为0的路径的条数)∗2
因为起点和终点不同所以要*2
my code
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
const int MAXN = 30005;
//邻接表
//----------------------------
struct Edge {
int u, v, val, order;
Edge() {}
Edge(int _u, int _v, int _val, int _order) {
u = _u, v = _v, val = _val;
order = _order;
}
};
int index;
map<string, int> mp;
vector<Edge> edge[MAXN];
void setIndex(string str) {
if(!mp.count(str))
mp[str] = ++index;
}
int getIndex(string str) {
return mp[str];
}
void init(int n) {
mp.clear();
index = 0;
for(int i = 0; i <= n; i++) {
edge[i].clear();
}
}
void addEdge(int u, int v, int val, int order) {
edge[u].push_back(Edge(u, v, val, order));
}
int lt[MAXN], rt[MAXN], color[MAXN];
void dfs(int u, int pre, int sum) {
int v, val, order;
for(int i = 0; i < edge[u].size(); i++) {
v = edge[u][i].v;
val = edge[u][i].val;
order = edge[u][i].order;
if(v == pre) continue;
lt[order] = ++index; color[index] = sum^val;
dfs(v, u, sum^val);
rt[order] = index;
}
}
//----------------------------
//----------------------------
int sumv[MAXN<<2], cover[MAXN<<2];
void pushUp(int o) {
sumv[o] = sumv[ls] + sumv[rs];
}
void pushDown(int o, int L, int R) {
if(cover[o]) {
int M = (L+R)/2;
cover[ls] ^= 1;
sumv[ls] = (M-L+1) - sumv[ls];
cover[rs] ^= 1;
sumv[rs] = (R-M) - sumv[rs];
cover[o] = 0;
}
}
void build(int o, int L, int R) {
cover[o] = 0;
if(L == R) {
sumv[o] = color[L];
return ;
}
int M = (L+R)/2;
build(ls, L, M);
build(rs, M+1, R);
pushUp(o);
}
int ql, qr;
void modify(int o, int L, int R) {
if(ql <= L && R <= qr) {
cover[o] ^= 1;
sumv[o] = (R-L+1) - sumv[o];
return ;
}
pushDown(o, L, R);
int M = (L+R)/2;
if(ql <= M) modify(ls, L, M);
if(qr > M) modify(rs, M+1, R);
pushUp(o);
}
//----------------------------
int n, m;
char str[15], str2[15], cmd[5];
void solve() {
scanf("%d", &n);
init(n);
for(int i = 1; i <= n; i++) {
scanf("%s", str);
setIndex(str);
}
int u, v, val;
for(int i = 1; i < n; i++) {
scanf("%s%s%d", str, str2, &val);
u = getIndex(str);
v = getIndex(str2);
addEdge(u, v, val, i);
addEdge(v, u, val, i);
}
//dfs序,求区间
index = 0;
dfs(1, -1, 0);
build(1, 1, n);
scanf("%d", &m);
while(m--) {
scanf("%s", cmd);
if(cmd[0] == 'Q') {
printf("%lld\n", 2ll*(n-sumv[1])*sumv[1]);
}else {
int order;
scanf("%d" ,&order);
ql = lt[order], qr = rt[order];
modify(1, 1, n);
}
}
}
int main() {
//freopen("in.txt", "r", stdin);
int T, cas = 1;
scanf("%d", &T);
while(T--) {
printf("Case #%d:\n", cas++);
solve();
}
return 0;
}