2020杭电多校联合训练6 1006 hdu-6832 A Very Easy Graph Problem

题目链接:点击跳转

Problem Description
2020杭电多校联合训练6 1006 hdu-6832 A Very Easy Graph Problem_第1张图片

Input
2020杭电多校联合训练6 1006 hdu-6832 A Very Easy Graph Problem_第2张图片

Output
在这里插入图片描述

Sample Input

4
3 2
0 1 0 
3 1
3 2
7 6
1 0 1 0 0 1 0
1 2
2 3
2 4
1 5
5 6
5 7
7 6
1 0 1 1 0 1 0
1 2
2 3
2 4
1 5
5 6
5 7
4 3
1 0 1 0
1 2
3 4
2 3

Sample Output

10
468
512
28

题目大意:
这题要求在图中任意两点的最短路的距离和(只有前面的点的值为1和后面的点的值为0的情况下才需要加上)

解题思路:
因为这题的边权是2的i次,那么不难想到只要前面有能联通的边就不会去选择后面的边(因为前面的边全部选上都不会有后面的边的权重来的大),所以可以用并查集判断两个点是不是在一个集合里,不是就建边,然后把两个点的祖先并在一起,然后通过dfs遍历建好的图,用dp数组记录每条边过去的所有0的点和1的点的数量,那么显然答案就是右边的1的个数乘上左边的0的个数和右边的0的个数乘上左边的1的个数,这个就是经过这条边的数量,在乘上这条边的权重,就能够得出这条边所产生的贡献,dfs遍历完所有的边后记录每条边的贡献,加在一起就是答案.(不用并查集,直接用vis数组标记每个点有没有被标记过,有一个点没被标记过就建边,虽然这个思路显然是错的,但是可能是数据不够,是可以过的)

代码如下:

#include

using namespace std;
typedef long long ll;
#define endl '\n'
const ll MOD = 1e9 + 7;
const int MAXN = 1e5 + 5e2;
int n, m, bin[MAXN], head[MAXN], cnt, op[MAXN], pow2[MAXN << 1];
ll res, one, zero, dp[MAXN][2];
struct Edge {
    int to, nxt;
    ll val;
} e[MAXN << 2];

inline int find(int x) {
    return bin[x] == x ? x : bin[x] = find(bin[x]);
}

inline void add(int from, int to, ll val) {//建双向边
    e[cnt] = Edge{to, head[from], val};
    head[from] = cnt++;
    e[cnt] = Edge{from, head[to], val};
    head[to] = cnt++;
}

inline void merge(int u, int v, ll val) {
    int fu = find(u);
    int fv = find(v);
    if (fu != fv) {//两个点并未联通就建边
        add(u, v, val);
        bin[fu] = fv;
    }
}

inline void init() {//初始化
    memset(dp, 0, sizeof(dp));
    memset(head, -1, sizeof(head));
    cnt = one = zero = 0;
}

inline void dfs(int now, int from) {
    dp[now][op[now]]++;//记录当前点的状态
    for (int i = head[now]; i != -1; i = e[i].nxt) {
        int to = e[i].to;
        if (to == from)continue;
        dfs(to, now);//先往下走完然后记录0的点和1的点的数量
        dp[now][0] += dp[to][0];
        dp[now][1] += dp[to][1];
        /**
         * 每条边产生的值是由需要计算的个数乘上边权得出的,每个左边的0都能和这条边右边的1需要计算一
         * 次,左1右0同理,那么拿输入时记录的总数减去右边的个数就能得出左边的点的数量,相乘再乘上边权
         * 每次加上这个值,遍历完每条边就能算出答案
         */
        res = ((res + 1LL * dp[to][0] * (one - dp[to][1]) % MOD * e[i].val % MOD) + MOD) % MOD;
        res = ((res + 1LL * dp[to][1] * (zero - dp[to][0]) % MOD * e[i].val % MOD) + MOD) % MOD;
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie();
    cout.tie();
    pow2[0] = 1;
    for (ll i = 1; i < MAXN; i++) {
        pow2[i] = (pow2[i - 1] << 1) % MOD;
    }
    int Case;
    cin >> Case;
    while (Case--) {
        init();
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            cin >> op[i];
            if (op[i] & 1) {
                one++;
            } else {
                zero++;
            }
            bin[i] = i;
        }
        for (int i = 1; i <= m; i++) {
            int u, v;
            cin >> u >> v;
            merge(u, v, pow2[i]);
        }
        res = 0;
        dfs(1, -1);
        cout << res << endl;
        cout.flush();
    }
    return 0;
}

你可能感兴趣的:(补题)