【2019ICPC西安邀请赛 J.And And And】树形DP+map

2019ICPC西安邀请赛 J.And And And

题意

给你一颗带有边权的树,问所有简单路径包含的异或值为0的简单路径的总条数。

做法

首先这道题正向做不好做,我们要考虑反向计算贡献,也就是计算每条异或值为0的路径被计算了多少次。
可以发现每个异或值为0的路径u->v,只需要保证u到根节点的异或值等于v到跟节点的异或值即可。
于是我们把边权转换为点权,定义sz[i]表示以i为根的子树大小。
对于不互为祖先的u,v,我们发现这个路径对答案的贡献就是sz[u]*sz[v]。
因为u子树内每一个节点到v子树内每一个节点都会经过这条路径。
之后我们发现对于u是v祖先或者v是u祖先的情况,这个贡献是不对的。
所以我们首先减去错误的贡献,用一个map统计当前链上每种权值的sz之和即可。
之后我们需要加上正确的贡献,对于u是v祖先的情况,我们发现贡献就是sz[v]×(n-u通往v方向的子树大小),这个画个图就可以理解。
于是我们用另一个map维护当前链上的这个信息即可。
这里用unordered_map,时间复杂度为O(n)。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<unordered_map>
using namespace std;

typedef long long ll;
typedef pair <int,ll> pil;

const int maxn = 1e5+5;
const int Mod=1000000007;

#define Se second
#define Fi first
#define pb push_back

unordered_map<ll,ll>mp;
unordered_map<ll,ll>mp2;
unordered_map<ll,ll>sum;
ll sz[maxn],val[maxn],ans,w;
vector<pil> G[maxn];
int n,fa;


void dfs(int rt,ll XOR)
{
    val[rt]=XOR,sz[rt]=1;
    for(int i=0;i<G[rt].size();i++)
    {
        dfs(G[rt][i].Fi,XOR^G[rt][i].Se);
        sz[rt]=sz[rt]+sz[G[rt][i].Fi];
    }
}
void dfs2(int rt)
{
    ans=(ans-1LL*sz[rt]*mp[val[rt]]%Mod+1LL*sz[rt]*mp2[val[rt]]%Mod+Mod)%Mod;
    mp[val[rt]]=(mp[val[rt]]+sz[rt])%Mod;
    for(int i=0;i<G[rt].size();i++)
    {
        int to=G[rt][i].Fi;
        mp2[val[rt]]=(mp2[val[rt]]+n-sz[to]+Mod)%Mod;
        dfs2(to);
        mp2[val[rt]]=(mp2[val[rt]]-n+sz[to]+Mod)%Mod;
    }
    mp[val[rt]]=(mp[val[rt]]-sz[rt]+Mod)%Mod;
}
int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d%lld",&fa,&w);
        G[fa].pb(pil(i,w));
    }
    dfs(1,0);
    dfs2(1);
    for(int i=1;i<=n;i++)
    {
        ans=(ans+1LL*sz[i]*sum[val[i]])%Mod;
        sum[val[i]]=(sum[val[i]]+sz[i])%Mod;
    }
    printf("%lld\n",ans);
    return 0;
}

发布了299 篇原创文章 · 获赞 117 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_38891827/article/details/90665224