牛客練習賽32 B-Xor-Path 技巧題

原題:題目鏈接

  題目大意:

  給定一棵樹,求樹上所有點對之間路徑上的值的異或和,即對所有點對( i , j )n >= i >= 1,n >= j >= 1且i != j,這些所有的點對間路徑上的值的異或和,的異或和

  解題大意:

  根據Xor的性質,同一個數抑或偶數次為0,奇數次為它本身,所以只要知道一個節點值被Xor的次數,就知道它對答案有沒有貢獻,最後把那些被Xor奇數次的值,即Xor後不為0的值,求它們的Xor和就是答案。

  這裡DFS一遍,就可以算出每個節點被Xor多少次。一個節點被Xor的次數分成兩部分,從它出發到其他(n-1)個節點的路徑數,和經過它的路徑數。算經過它的路徑時,就有點技巧:

  怎樣不重不漏地算出這些節點經過它的兩兩之間的路徑數。對於一個結點,搜它的子樹,記已經搜到的節點數為sum,每次搜一個枝,得到temp個點,temp*sum,就是這temp個節點與sum個節點間經過本節點的路徑數,然後把temp加到sum裡面去,繼續搜下一個枝,進行同樣的操作即可,然後再考慮子樹節點與非子樹節點間的路徑。仔細思考就能發現這就不重不漏了。之前想到複雜度很高的方法,TLE了

AC代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int N = 1e6;
int n,m,cnt;
int head[N];
int val[N];
bool f[N];
struct Edge
{
    int to,next;
}e[N];

void add(int u,int v)
{
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
int dfs(int now,int fa)
{
    int sum=0;
        long long t=0;
    int temp;
    for(int i=head[now];i!=-1;i=e[i].next)
    {
        if(e[i].to!=fa)
        {
            temp=dfs(e[i].to,now);    //每次搜索上來
            t+=(temp*sum)%2;    //與之前搜到的和相乘,即可,只判斷奇偶,%2即可
            sum+=temp;
        }
    }
        t=(t+n-1)%2;    //從本節點出發的路徑
        t+=((n-sum-1)*sum)%2;    //所有從子樹節點出發,到非子樹節點的路徑
    if(t%2)
        f[now]=1;
    return sum+1;
}
int main()
{
    memset(f,0,sizeof(f));
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    int u,v;
    for(int i=1;i<n;++i)
    {
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;++i)
        scanf("%d",&val[i]);
    dfs(1,-1);
    int a=0;
    for(int i=1;i<=n;++i)
    {
        if(f[i])
        a^=val[i];
    }
    printf("%d
",a);
}    
原文地址:https://www.cnblogs.com/Lin88/p/10073267.html