HDU 5416
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5416
题意:
给一棵树,每条边有权值。问f(u,v) = s的(u,v)对数有多少对。u<=v。
思路:
赛后过。
刚开始以为是什么不知道的算法,上网看标题是树形dp或者dfs。回来想了又想……不对啊,树形dp也不能dp 10^5 * 10^5。就算如此,怎么算两个叶子节点的路径……
题解给出这样的解释f(u,v) = f(1,u)^f(1,v)。秒懂。相同则去掉。
这说明异或运算是有性质的,异或运算是可以利用的!
源码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 400000;
int dp[MAXN];
int cnt, n;
int lv[MAXN];
int head[MAXN];
struct Edge
{
int u, v, val, ne;
Edge(){}
Edge(int _u, int _v, int _val){u = _u, v = _v, val = _val, ne = head[u];}
}edge[MAXN*2];
//int vis[MAXN];
void addedge(int u, int v, int val)
{
edge[cnt] = Edge(u, v, val);
head[u] = cnt++;
edge[cnt] = Edge(v, u, val);
head[v] = cnt++;
}
void DFS(int u, int fa, int val)
{
int now = head[u];
lv[u] = val;
// if(vis[u] == 0){
// vis[u] = 1;
// if(fa > 0)
// dp[val]++;
// }
while(now != -1){
int v = edge[now].v;
if(v != fa){
DFS(v, u, val ^ edge[now].val);
}
now = edge[now].ne;
}
}
void init()
{
memset(dp, 0, sizeof(dp));
DFS(1, -1, 0);
}
int main()
{
// freopen("1011.in", "r", stdin);
// freopen("my data 1011.out", "w", stdout);
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
int u, v, val;
cnt = 0;
memset(head, -1, sizeof(head));
for(int i = 0 ; i < n - 1 ; i++){
scanf("%d%d%d", &u, &v, &val);
addedge(u, v, val);
}
memset(vis, 0, sizeof(vis));
init();
for(int i = 1 ; i <= n ; i++)
dp[lv[i]]++;
int q;
scanf("%d", &q);
for(int i = 0 ; i < q ; i++){
int s;
scanf("%d", &s);
LL ans = 0;
for(int i = 1 ; i <= n ; i++){
LL temp = lv[i] ^ s;
ans += dp[temp];
}
LL tans = 0;
if(s == 0)
tans = n ;
printf("%I64d\n", (ans + tans) / 2);
}
}
return 0;
}
再贴一个错误的版本
#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>#include <cstdlib>#include <string>#include <iostream>using namespace std;#define LL long longconst int MAXN = 400000;int dp[MAXN];int cnt, n;int lv[MAXN];int head[MAXN];struct Edge{ int u, v, val, ne; Edge(){} Edge(int _u, int _v, int _val){u = _u, v = _v, val = _val, ne = head[u];}}edge[MAXN*2];int vis[MAXN];void addedge(int u, int v, int val){ edge[cnt] = Edge(u, v, val); head[u] = cnt++; edge[cnt] = Edge(v, u, val); head[v] = cnt++;}void DFS(int u, int fa, int val){ int now = head[u]; lv[u] = val; if(vis[u] == 0){ vis[u] = 1; if(fa > 0) dp[val]++; } while(now != -1){ int v = edge[now].v; if(v != fa){ DFS(v, u, val ^ edge[now].val); } now = edge[now].ne; }}void init(){ memset(dp, 0, sizeof(dp)); DFS(1, -1, 0);}int main(){// freopen("1011.in", "r", stdin);// freopen("my data 1011.out", "w", stdout); int t; scanf("%d", &t); while(t--){ scanf("%d", &n); int u, v, val; cnt = 0; memset(head, -1, sizeof(head)); for(int i = 0 ; i < n - 1 ; i++){ scanf("%d%d%d", &u, &v, &val); addedge(u, v, val); } memset(vis, 0, sizeof(vis)); init();// for(int i = 1 ; i <= n ; i++)// dp[lv[i]]++; int q; scanf("%d", &q); for(int i = 0 ; i < q ; i++){ int s; scanf("%d", &s); LL ans = 0; for(int i = 1 ; i <= n ; i++){ LL temp = lv[i] ^ s; ans += dp[temp]; } LL tans = 0; if(s == 0) tans = n ; printf("%I64d\n", (ans + tans) / 2); } } return 0;}
以及改进后版本
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 400000;
int dp[MAXN];
int cnt, n;
int lv[MAXN];
int head[MAXN];
int mmax;
struct Edge
{
int u, v, val, ne;
Edge(){}
Edge(int _u, int _v, int _val){u = _u, v = _v, val = _val, ne = head[u];}
}edge[MAXN*2];
int vis[MAXN];
void addedge(int u, int v, int val)
{
edge[cnt] = Edge(u, v, val);
head[u] = cnt++;
edge[cnt] = Edge(v, u, val);
head[v] = cnt++;
}
void DFS(int u, int fa, int val)
{
int now = head[u];
lv[u] = val;
mmax = max(val, mmax);
if(vis[u] == 0){
vis[u] = 1;
// if(fa > 0)
dp[val]++;
}
while(now != -1){
int v = edge[now].v;
if(v != fa){
DFS(v, u, val ^ edge[now].val);
}
now = edge[now].ne;
}
}
void init()
{
mmax = 0;
memset(dp, 0, sizeof(dp));
DFS(1, -1, 0);
}
int main()
{
// freopen("1011.in", "r", stdin);
// freopen("my data 1011 b.txt", "w", stdout);
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
int u, v, val;
cnt = 0;
memset(head, -1, sizeof(head));
for(int i = 0 ; i < n - 1 ; i++){
scanf("%d%d%d", &u, &v, &val);
addedge(u, v, val);
}
memset(vis, 0, sizeof(vis));
init();
// for(int i = 1 ; i <= n ; i++)
// dp[lv[i]]++;
// for(int i = 0 ; i <= mmax ; i++)
// printf("%d ", dp[i]);
// printf("dp\n");
int q;
scanf("%d", &q);
for(int i = 0 ; i < q ; i++){
int s;
scanf("%d", &s);
LL ans = 0;
for(int i = 1 ; i <= n ; i++){
LL temp = lv[i] ^ s;
ans += dp[temp];
}
LL tans = 0;
if(s == 0)
tans = n ;
printf("%I64d\n", (ans + tans) / 2);
}
}
return 0;
}
/*
9
2 1 2
3 1 0
4 3 7
5 4 3
6 2 2
7 2 5
8 4 8
9 6 2
9
3
2
5
2
0
5
2
7
4
*/
主要是DFS中对于(1,1)点也要加上,最后若s == 0时再加上所有点个数n。若不是,则会(1,1)会少算一次,最后ans/2时会得到错误答案。