题目链接:点击跳转
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;
}