hdu5739Fantasia(多校第二场1006) 割点+逆元

Fantasia

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Problem Description

Professor Zhang has an undirected graph G with n vertices and m edges. Each vertex is attached with a weight wi. Let Gi be the graph after deleting the i-th vertex from graph G. Professor Zhang wants to find the weight of G1,G2,...,Gn.

The weight of a graph G is defined as follows:

1. If G is connected, then the weight of G is the product of the weight of each vertex in G.
2. Otherwise, the weight of G is the sum of the weight of all the connected components of G.

A connected component of an undirected graph G is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in G.

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:

The first line contains two integers n and m (2≤n≤105,1≤m≤2×105) -- the number of vertices and the number of edges.

The second line contains n integers w1,w2,...,wn (1≤wi≤109), denoting the weight of each vertex.

In the next m lines, each contains two integers xi and yi (1≤xi,yi≤n,xi≠yi), denoting an undirected edge.

There are at most 1000 test cases and ∑n,∑m≤1.5×106.

Output

For each test case, output an integer S=(∑i=1ni⋅zi) mod (109+7), where zi is the weight of Gi.

Sample Input

1
3 2
1 2 3
1 2
2 3

Sample Output

20

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5739

题目描述:

  有n个节点,每个节点有个值,然后m条边构成可能不止一张张图,每张图的价值是每个节点的值的乘积,然后总的价值就是所有图的价值加起来。

现在要分别删除每个点,G1就是代表的删除编号为1的节点,所有图加起来的价值。 然后问你1*G[2] + 2*G[2] + ……+n*G[n]. 最后的值膜一个1e9+7。

题解:

  这个题很显然就是求割点,如果不是割点,就直接删除这个点就好了,如果是割点就复杂一点,就需要将该点的子树中最多能够访问到该点的子树的值给处理出来。然后把子树分开,另外处理就好了,还是看代码分析吧。道理是这么说,但是中途写错了好多东西TAT,对着数据改到现在...唉....

代码:

 #include<cstdio>
 #include<cmath>
 #include<iostream>
 #include<algorithm>
 #include<vector>
 #include<stack>
 #include<cstring>
 #include<queue>
 #include<set>
 #include<string>
 #include<map>
 #define inf 9223372036854775807
 #define INF 9e7+5
 #define PI acos(-1)
 using namespace std;
 typedef long long ll;
 typedef double db;
 const int maxn = 1e5 + 5;
 const int mod = 1e9 + 7;
 const db eps = 1e-9;
 ll va[maxn], w[maxn], Sum, ans[maxn];
 int pre[maxn], dfs_tim, tot, n, m, low[maxn], t, vep[maxn];
 bool vis[maxn];
 vector<int> G[maxn];

 void init() {
     memset(vis, false, sizeof(vis));
     memset(pre, 0, sizeof(pre));
     Sum = tot = dfs_tim = 0;
     for (int i = 1; i <= n; i++) G[i].clear();
 }
 //快速幂,求逆元用
 ll pow_mod(ll a, ll b, ll p) {
     ll ret = 1;
     while(b) {
         if(b & 1) ret = (ret * a) % p;
         a = (a * a) % p;
         b >>= 1;
     }
     return ret;
 }
 //费马小定理求的逆元
 ll inv(ll x) {
     return pow_mod(x, mod-2, mod);
 }
 // 先写好,懒得每次模
 void add(ll &x, ll y) {
     x = x + y;
     x = (x + mod) % mod;
 }
 // 主要是把每张图的价值处理出来
 void Find(int x) {
     va[x] = w[x];
     for (int i = 0; i < G[x].size(); i++) {
         int u = G[x][i];
         if (vis[u]) continue;
         vis[u] = true; Find(u);
         va[x] = va[x] * va[u] % mod;
     }
 }

 ll dfs(int x, int fa, int root) { //当前节点,父节点和根节点
     low[x] = pre[x] = ++dfs_tim; //pre数组记录访问的时间
     ans[x] = inv(w[x]); //删除此时访问的节点
     int cld = 0; ll sum = 0, res = w[x], pro = 1;
     for (int i = 0; i < G[x].size(); i++) {
         int u = G[x][i];
         if (!pre[u]) {
             cld++;
             ll tmp = dfs(u, x, root); //tmp返回的是对于u这颗子树的价值
             low[x] = min(low[x], low[u]); //更新x节点所能访问的最早的祖先
             if (low[u] >= pre[x]) {  //如果u这颗子树所能访问的是x,那么说明x节点被删除,u这颗子树会被分开
                 add(sum, tmp);  //sum表示的是x节点被删除后,x会被分开的子树的价值之和
                 ans[x] = ans[x] * inv(tmp) % mod;  //和上面删除节点一样,表示将这颗子树删除
             }
             res = res * tmp % mod;  //求子树的价值
         }
         else if (u != fa) low[x] = min(low[x], pre[u]); //对于访问比当前节点早的节点,更新能访问的最早节点
     }                                      //tt表示的是除了这幅图,其它图的价值之和
     ll tt = (Sum - va[root] + mod) % mod; //va[roor]*ans[x]中ans[x]已经是逆元了,所以这句话
     ans[x] = va[root] * ans[x] % mod;    //表示的是将x节点和会分开的子树 删除后该图的值
     if (fa == -1 && ans[x] == 1) ans[x] = 0; //对于一张图,如果他的子节点全部被删除了,我们
                                           //求到的ans[x]是1,但事实上应 该是0,所以
                                                //需要特判一下,比如这样一张图 1 - 2, 1 - 3.
     add(ans[x], tt); add(ans[x], sum); //将其他图和删除的子树加起来
     if (fa == -1) {
         if (cld == 1) { //对于最开始的祖先,如果他只有一个儿
                        //子,那么他不是割点,学割点应该都学过QAQ
             ans[x] = va[root] * inv(w[x]) % mod;
             add(ans[x], tt);
         }
         else if (G[x].size() == 0) {
             ans[x] = tt; //如果这是一个孤立点,删除后就直接是其他图的值
         }
     }
     return res;
 }

 void solve() {
     cin >> n >> m;
     init();
     for (int i = 1; i <= n; i++) scanf("%I64d", &w[i]);
     for (int i = 1; i <= m; i++) {
         int u, v; scanf("%d%d", &u, &v);
         G[u].push_back(v);
         G[v].push_back(u);
     }
     for (int i = 1; i <= n; i++) {
         if (vis[i]) continue;
         vis[i] = true;
         vep[++tot] = i; Find(i); //vep数组用来存每次要访问的图的开始节点
         add(Sum, va[i]); //所有图的总价值,va[i]就代表了这张图的总价值
     }
     for (int i = 1; i <= tot; i++) {
         dfs(vep[i], -1, vep[i]); //-1位置代表的父节点,对于最开始的点的父亲设为-1
     }
     ll pri = 0;
     for (ll i = 1; i <= n; i++) {
         add(pri, i*ans[i]%mod); //求出最后的值
     }
     cout << pri << endl;
}
int main() {
    //cin.sync_with_stdio(false);
   // freopen("tt.txt", "r", stdin);
    //freopen("hh.txt", "w", stdout);
    cin >> t;

    while (t--)
        solve();
    return 0;
}

  

时间: 2025-01-02 14:55:04

hdu5739Fantasia(多校第二场1006) 割点+逆元的相关文章

hdu_6050: Funny Function (2017 多校第二场 1006) 【找规律】

题目链接 暴力打个表找下规律就好了,比赛时看出规律来了倒是,然而看这道题看得太晚了,而且高中的那些数列相关的技巧生疏了好多,然后推公式就比较慢..其实还是自身菜啊.. 公式是 #include<bits/stdc++.h> using namespace std; typedef long long LL; const LL mod=1e9+7; LL qpow(LL x,LL n) //求x^n%mod { LL ret=1; for(; n; n>>=1) { if(n&

多校第二场 简单排序计算

思路:先按交叉相乘之差排序好了计算就行了. #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <map> #include <cstdlib> #include <queue> #include <stack> #include <vector> #include <ctype.

2015 多校赛 第二场 1006 (hdu 5305)

Problem Description There are n people and m pairs of friends. For every pair of friends, they can choose to become online friends (communicating using online applications) or offline friends (mostly using face-to-face communication). However, everyo

2018-3-17-湖南多校第二场

湖南多校第2场 A:太水 队友直接秒了 C: Hedwig's Ladder 队友打了个表,然后一眼规律 类似于斐波拉契 1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <cmath> 5 #include <algorithm> 6 #include <set> 7 #include <iostream> 8 #include

hdu_6055 : Regular polygon (2017 多校第二场 1011) 【计算几何】

题目链接 有个结论: 平面坐标系上,坐标为整数的情况下,n个点组成正n边形时,只可能组成正方形. 然后根据这个结论来做. 我是先把所有点按照 x为第一关键字,y为第二关键字 排序,然后枚举向量 (p[i]->p[j]) (j>i),只判断这个向量左侧可否存在两个点与它一起构成一个正方形.这样算的结果是,计数每个正方形时,它的靠右和靠下的两条边都会为ans贡献一个单位,所以最后ans要除以2. #include<bits/stdc++.h> using namespace std;

hdu 5305 Friends(2015多校第二场第6题)记忆化搜索

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5305 题意:给你n个人,m条关系,关系可以是online也可以是offline,让你求在保证所有人online关系的朋友和offline关系的朋友相等的情况下,这样的情况有多少种. 思路:因为online关系和offline关系的人数相等,而且m最多才28,所以只要枚举每个人的一半的关系是否符合要求即可,而且根据题意m是奇数或者有一个人的总关系为奇数那么就没有符合要求的情况,这样可以排除很多情况.

2015多校第二场 1004( Delicious Apples )

题意:有一条圆形的路,你的仓库在0点,这条路长l,然后有n个苹果树,每个数的坐标是xi(顺时针),每棵树上有ai个苹果.你有个篮子,能装k个苹果,问你用这个篮子将所有苹果装回仓库所走的最短路为多少? 1≤n,k≤105,ai≥1,a1+a2+...+an≤105 1≤L≤109 0≤x[i]≤L 请特别注意上面的苹果个数的条件.因为我的学长 就是从这个条件成功做出了这道题,orz! 因为苹果数不超过10^5,所以将每个苹果当作点. 用一个数组pos记录每个苹果距0点的距离,排序后,然后就可以用另

多校第二场 1004 hdu 5303 Delicious Apples(背包+贪心)

题目链接: 点击打开链接 题目大意: 在一个周长为L的环上.给出n棵苹果树.苹果树的位置是xi,苹果树是ai,苹果商店在0位置,人的篮子最大容量为k,问最少做多远的距离可以把苹果都运到店里 题目分析: 首先我们能够(ˇ?ˇ) 想-,假设在走半圆之内能够装满,那么一定优于绕一圈回到起点.所以我们从中点将这个圈劈开.那么对于每一个区间由于苹果数非常少,所以能够利用belong[x]数组记录每一个苹果所在的苹果树位置,然后将苹果依照所在的位置排序,那么也就是我们知道每次拿k个苹果的代价是苹果所在的最远

HDU 5301 Buildings(2015多校第二场)

Buildings Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 759    Accepted Submission(s): 210 Problem Description Your current task is to make a ground plan for a residential building located