Fantasia
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 375 Accepted Submission(s): 30
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 inG.
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 inG.
Input
There are multiple test cases. The first line of input contains an integerT,
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.
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.
题目大意:给你一个森林,森林中的每个点有固定的价值,森林中树的价值为树中所有点的乘积,森林的价值为森林中所有树价值的总和,从1到n每次删除一个点,设删完点后森林的价值*对应得点的序号为Gi,问Gi(i从1到n)的总和是多少
解题思路: 预处理:预处理出每个树的价值,将每一个无根树通过DFS变为有根树,在DFS的过程中求出每一个点及其下所有子节点的乘积存储到数组val中,求出每一个点的dfs序存储到pre中,求出每一个点的low值,low值就是此点能连接到的dfs序中标号最小的点,存储到low中(不懂low值或pre是什么的请见刘汝佳入门经典-训练指南上的312页或者百度双连通分量,推荐前者)
具体操作:对于某个树,当要删除树中点 i 时,判断其所有子节点low[j]是否大于等于pre[i],如果是则说明删除点 i 后其子节点j和其下的所有点会变成单独的连通块,即刚才预处理出的val[j],用一个值ans2统计所有的val[j] ,再用一个值ans1表示删除点 i 后剩余节点的价值,用树的价值乘以i价值 的逆元和所有val[j]的逆元 就可以了。多个树稍微变变就好了
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int n,m;
int w[100010];//每个节点的权重
vector<int> mapp[100010];//存图
vector<int> son[100010];//每个节点的儿子
int pre[100010];//节点的DFS序
int low[100010];//每个节点能连接到的最小的DFS序标号
ll val[100010];//每个节点下的价值
ll treeVal[100010];//每个树的价值
int tree[100010];//每个节点对应的树的标号
bool root[100010];//节点是否为根节点,根节点会乘所有点的逆元,因为乘以所有点的逆元后想要得到的是0,但是为1,所有特殊判断
ll ans,ans1,ans2;
int treeNumber;
int clock;
void init()
{
clock=1;
memset(pre,0,sizeof(pre));
memset(root,0,sizeof(root));
treeNumber=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=0;i<=n;i++)
{
mapp[i].clear();
son[i].clear();
}
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
mapp[a].push_back(b);
mapp[b].push_back(a);
}
}
ll dfs(int u,int fa)
{
int lowu=pre[u]=clock++;//DFS序,1开始
tree[u]=treeNumber;
ll sum=w[u];
for(int i=0;i<mapp[u].size();i++)
{
int v=mapp[u][i];
if(!pre[v])
{
int lowv=dfs(v,u);
lowu=min(lowv,lowu);
son[u].push_back(v);
sum=(sum*val[v])%mod;
}
else
{
lowu=min(lowu,pre[v]);
}
}
val[u]=sum;
return low[u]=lowu;
}
ll qpow(ll a,ll b)
{
ll ans=1;
a=a%mod;
while(b)
{
if(b&1)
ans=(ans*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return ans;
}
ll inverse(ll a)
{
return qpow(a,mod-2);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
for(int i=1;i<=n;i++)
{
if(!pre[i])
{
dfs(i,-1);
treeVal[treeNumber]=val[i];
root[i]=true;//i为根
treeNumber++;//树的个数
}
}
ll sum=0;
for(int i=1;i<treeNumber;i++)
sum+=treeVal[i];
ans=0;
for(int i=1;i<=n;i++)
{
ans2=0;//变成联通快的价值和
ans1=treeVal[tree[i]]; //出去ans2和i的价值和
ll ss=sum-ans1;//其余树的价值
ans1=ans1*inverse(w[i])%mod;
for(int j=0;j<son[i].size();j++)
{
int v=son[i][j];
if(low[v]>=pre[i])
{
ans1=ans1*inverse(val[v])%mod;
ans2=(ans2+val[v])%mod;
}
}
if(root[i])
ans1-=1;//根节点要减1,如果能计算回根,此时ans1一定为1!这时候不要他,因为他给删掉了
ans=(ans+(ans1+ans2+ss)*i%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}

本文解析了一道关于图论的竞赛题目,涉及无向图、连通性、DFS及双连通分量等核心概念。介绍了如何通过预处理确定每个树的价值,并利用DFS将无根树转换为有根树进行高效计算。
532

被折叠的 条评论
为什么被折叠?



