2019西安邀請賽-And And And(樹上點分治)

題目鏈接

思路

首先統計當前結點的子樹有多少個結點sum(包括自己),以及統計從根走到當前結點的xor,因爲兩點的xor值和從兩點走到根的xor值是一樣的。

情況分爲兩種,一種是兩個點在同一條鏈上,一種是在兩條鏈上。

用map 維護 同鏈或者不同鏈的xor相等的個數。

在不同鏈的情況,在先序位置求ans,在後序map加上數值,這樣以後的點每次求and的時候,map中一定是與該點不同鏈的點貢獻的值,而且題目中u<v,有方向,所以map中不同鏈,而且都是該點DFS序前面的點,符合要求。

同一條鏈的情況,在先序位置求ans,在for循環中的dfs前map加上數值,dfs後減去數值,類似於走迷宮的vis標記。這樣dfs前加標記,dfs後清除標記,就會使走的路是一條長鏈,所以map中的都是一條鏈上的。

參考:詳細的推導過程


#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1000010;
const int mod = 1e9+7;

struct Edge
{
    int to,next; ll w;
}edge[N];
int head[N],tot=0,n;
void addEdge(int from,int to,ll w)
{
    edge[tot].to = to; edge[tot].w = w;
    edge[tot].next = head[from];
    head[from] = tot++;
}

ll Xor[N],sum[N],ans;
map <ll,ll> mp;
void dfsnum(int u)
{
    sum[u] = 1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        ll w = edge[i].w;
        Xor[v] = Xor[u]^w;
        dfsnum(v);
        sum[u] += sum[v];
    }
}

void dfs1(int u)//不同鏈
{
    ans = (ans + sum[u]*mp[Xor[u]]%mod)%mod;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        dfs1(v);
    }
    mp[Xor[u]] = (mp[Xor[u]] + sum[u])%mod;
}

void dfs2(int u)//同鏈
{

    ans = (ans + sum[u]*mp[Xor[u]]%mod)%mod;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v = edge[i].to;
        ll t = sum[1]-sum[v];
        mp[Xor[u]] = (mp[Xor[u]] + t)%mod;
        dfs2(v);
        mp[Xor[u]] = (mp[Xor[u]] - t + mod)%mod;
    }
}

int main()
{
    tot = 0;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    int fa; ll w;
    for(int i=2;i<=n;i++){
        scanf("%d%lld",&fa,&w);
        addEdge(fa,i,w);
    }
    dfsnum(1);
    mp.clear();
    dfs1(1);
    mp.clear();
    dfs2(1);
    printf("%lld\n",ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章