传送门:点击打开链接
题意:给你一棵树,然后Q个问题,每个问题询问有存在多少个f(u,v)等于s其中u<=v,f(u,v)表示节点u到节点v所有边的权值异或和
思路:这道题我觉得出的非常好,运用了位异或的一些妙用。f(u,v)=f(1,u)^f(1,v),因为假设u,v的lca是p,那么1~p这些节点都是公用的,这部分被异或了两次,就抵消了
然后对于Q次问题,假设某一次问题是s,则有s=f(u,v)=f(1,u)^f(1,v)。那么有f(1,v)=s^f(1,u),也就是说,如果我们DFS到节点u,算出了f(1,v),那么对于某一个s,我们就需要算出f(1,v)等于多少,然后再通过cnt数组知道f(1,v)有多少个,把这个值累加到s这个查询的答案中去,就这样DFS遍历完整个树,答案就离线处理完了。复杂度O(Q*N)
trick:当出现下面这种数据的时候
1 10 1 2 0 2 3 0 3 4 0 4 5 0 5 6 0 6 7 0 7 8 0 8 9 0 1 0
#include<map> #include<set> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 2e5 + 5; const int INF = 0x3f3f3f3f; int Head[MX], Next[MX], rear; struct Edge { int u, v, cost; } E[MX]; void edge_init() { rear = 0; memset(Head, -1, sizeof(Head)); } void edge_add(int u, int v, int cost) { E[rear].u = u; E[rear].v = v; E[rear].cost = cost; Next[rear] = Head[u]; Head[u] = rear++; } LL ans[MX]; int cnt[MX], Q[MX], Qt; void DFS(int u, int f, int s) { cnt[s]++; for(int j = 1; j <= Qt; j++) { ans[j] += cnt[s ^ Q[j]]; } for(int i = Head[u]; ~i; i = Next[i]) { int v = E[i].v; if(f == v) continue; DFS(v, u, s ^ E[i].cost); } } int main() { int T, n;//FIN; scanf("%d", &T); while(T--) { edge_init(); memset(cnt, 0, sizeof(cnt)); memset(ans, 0, sizeof(ans)); scanf("%d", &n); for(int i = 1; i <= n - 1; i++) { int u, v, cost; scanf("%d%d%d", &u, &v, &cost); edge_add(u, v, cost); edge_add(v, u, cost); } scanf("%d", &Qt); for(int i = 1; i <= Qt; i++) { scanf("%d", &Q[i]); } DFS(1, -1, 0); for(int i = 1; i <= Qt; i++) { printf("%I64d\n", ans[i]); } } return 0; }