hdu 6832 A Very Easy Graph Problem 构造树+dfs

题意:

给你一个n个点m条边的图,对于第i条边,它的长度是2i,对于每一个顶点,它不是0类型,就是1类型。你需要找出来对于所有的“两个不同类型的点之间最短距离”的和

 

题解(参考:https://blog.csdn.net/wayne_lee_lwc/article/details/107851431):

因为20+21+22<23,即20+21+...+2n-1<2n

所以如果第i条边连接的两个点已经联通,我们就不需要用这条边。所以这里用并查集判断下

 

后面我们用

 

sum,以该节点为根的子树中所有黑白点对的距离和
dp[0][0],子树中所有黑节点到该节点的距离和
dp[0][1],子树中黑节点的数量
dp[1][0],子树中所有白节点到该节点的距离和
dp[1][1],子树中白节点的数量
color,节点颜色

 

 

dfs先统计一下最小的子树上上面个变量的值,然后再上升到更大的子树上进行综合统计

 

 

代码+注释:

 

#include<stack>
#include<queue>
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int N = 1e6 + 50;
const long long mod = 1e9 + 7;
struct Edge
{
    ll point;
    ll next;
    long long w;
} nxt[N];
struct Node
{
    ll type;
    long long dp[2][2];
    long long sum;
} node[N];
ll fa[N];
ll head[N];
ll T,n,m,tot;
ll finds(ll x)
{
    if(x!=fa[x])
    {
        ll y=finds(fa[x]);
        return fa[x]=y;
    }
    return x;
}
long long ppow(ll p)
{
    long long ans = 1;
    long long pow = 2;
    while(p)
    {
        if(p & 1) ans = (ans * pow) % mod;
        p >>= 1;
        pow = (pow * pow) % mod;
    }
    return ans;
}
void add_edge(ll x,ll y,long long w)
{
    nxt[++tot] = {y,head[x],w};
    head[x] = tot;
}
/*
sum,以该节点为根的子树中所有黑白点对的距离和
dp[0][0],子树中所有黑节点到该节点的距离和
dp[0][1],子树中黑节点的数量
dp[1][0],子树中所有白节点到该节点的距离和
dp[1][1],子树中白节点的数量
color,节点颜色
*/
void dfs(ll k,ll f)
{
    node[k].dp[node[k].type][1] = 1;

    for(ll i = head[k],j; i; i = nxt[i].next)
    {
        j = nxt[i].point;
        if(j == f) continue;
        dfs(j,k);
        node[k].dp[0][0] = (node[k].dp[0][0] + node[j].dp[0][0] + (node[j].dp[0][1] * nxt[i].w) % mod) % mod;
        node[k].dp[1][0] = (node[k].dp[1][0] + node[j].dp[1][0] + (node[j].dp[1][1] * nxt[i].w) % mod) % mod;
        node[k].dp[0][1] += node[j].dp[0][1];
        node[k].dp[1][1] += node[j].dp[1][1];
        node[k].sum = (node[k].sum + node[j].sum) % mod;
    }
    long long sum0 = node[k].dp[0][0];
    long long cnt0 = node[k].dp[0][1];
    long long sum1 = node[k].dp[1][0];
    long long cnt1 = node[k].dp[1][1];
    long long w;
    /*
    比如k树下面有i,j两颗子树,那么i树上的白色点到j树上的黑色点的距离我们可以用:
    i树上的白色点到k树的距离加上j树上黑色点到k树的距离
    */
    for(ll i = head[k],j; i; i = nxt[i].next)
    {
        j = nxt[i].point;
        if(j == f) continue;
        w = nxt[i].w;

        //k树上的黑色节点数量,与子树j的白色节点,所有黑白队的距离和
        node[k].sum = (node[k].sum + ((cnt0 - node[j].dp[0][1]) * (node[j].dp[1][0] + w * node[j].dp[1][1])) % mod) % mod;
        //k树上的白色节点数量,与子树j的黑色节点,所有黑白队的距离和
        node[k].sum = (node[k].sum + ((cnt1 - node[j].dp[1][1]) * (node[j].dp[0][0] + w * node[j].dp[0][1])) % mod) % mod;
    }
}
int main()
{
    cin >> T;
    while(T--)
    {
        tot = 1;
        scanf("%lld%lld",&n,&m);
        for(ll i = 1; i <= n; i++)
        {
            node[i] = {0,0,0,0,0,0};
            head[i] = 0;
            scanf("%lld",&node[i].type);
            fa[i] = i;
        }
        for(ll i = 1; i <= m; i++)
        {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            //加了下面这个判断,那么1就肯定是我们构造的树的根节点
            if(x>y) swap(x,y);
            if(finds(x) == finds(y))
                continue;

            add_edge(x,y,ppow(i));
            add_edge(y,x,ppow(i));
            fa[fa[y]] = fa[x];
        }
        dfs(1,0);
        cout << node[1].sum << endl;
    }
}

 

posted @ 2020-08-08 11:40  kongbursi  阅读(128)  评论(0编辑  收藏  举报