题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6832
题意:在一个n个结点,m条边的无向连通图中,且第i条边的权值为2i,每个结点有一个值,为1或者0。d(i,j)表示结点i到结点j之间的最短距离。对所有节点求所有的可能配对形式d(i,j)*[a[i]==1^a[0]==0]的和,最后对1e9+7求余。
输入:第一行t,表示样例个数。每个样例第一行两个整数n,m。接下来一行n个整数,表示结点的权值,接下来m行,每行u,v表示u,v之间存在一条边
输出:每个样例输出一个整数
题解:在这里我们需要注意这个权值是比较特殊的,也就是如果第i条边(权值为2i)是连接结点j和结点k,但是在这之前j和k已经相连了,那么这条边(权值2i)永远都不会被最短路经过,因为前i-1条边的总权值是2i-2,小于2i.所以我们便可以将这个无向连通图转换为一棵最小生成树(既是最小生成树也是任意两个结点之间的距离都是最小)。这里的n,m都是1e5的数量级别,所以如果暴力求两个结点的最短路那么肯定会超时。所以我们可以转换一下思路,求每条边的贡献值,也就是如果这条边最后会出现在最短路中,我们需要计算的是在最短路中出现了多少次,可以求出这条边两边各自的1和0的数量,那么这条边在最短路中的次数就是left_0*right_1+left_1*right_0,那么这条边的在最后的结果中的贡献就是(left_0*right_1+left_1*right_0)*这条边的权值。那么现在主要的就是求出每条边两边的1和0的数量,可以使用dfs进行求解。
AC代码:
#include<iostream> #include<vector> using namespace std; #define ll long long int const ll N=2e5+5; const ll mod=1e9+7; vector<pair<int,ll> >E[N]; int fa[N],a[N],sum0,sum1,k; struct edge{ int p0,p1; ll w; }b[N]; struct edge operator+(struct edge b1,struct edge b2){//重载运算符 struct edge b3; b3.p0=b1.p0+b2.p0; b3.p1=b1.p1+b2.p1; b3.w=0; return b3; } int find(int x){ if(x==fa[x]) return x; else return fa[x]=find(fa[x]); } void merge(int x,int y){ int rx=find(x),ry=find(y); fa[rx]=ry; } struct edge dfs(int now,int pre){ int size=E[now].size(); pair<int,ll>pa; int tem=k;//使用数组存储遍历后边的信息 k++; ll w=0; for(int i=0;i<size;i++){ pa=E[now][i]; if(pre==pa.first){//当与上一次遍历的节点相同时,先存储这条边的权重 w=pa.second; continue; } b[tem]=b[tem]+dfs(pa.first,now); } b[tem].w=w;//存储的是now与pre两个结点之间的边的信息 if(a[now]==0) b[tem].p0++; else b[tem].p1++; return b[tem]; } int main(){ int t;cin>>t; int n,m; while(t--){ cin>>n>>m;sum0=0,sum1=0;a[0]=0;k=0; for(int i=1;i<=n;i++) E[i].clear(); //这里一定要有,之前没有,一直runtime error for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化。并查集用于求解最小生成树 for(int i=0;i<=n;i++) b[i].p0=0,b[i].p1=0,b[i].w=0; //初始化 for(int i=1;i<=n;i++){ //输入结点权值 cin>>a[i]; if(a[i]==1) sum1++; //记录所有的1和0的数量。到时就可以只计算边的一边的1和0的数量就可以了 else sum0++; } int u,v; ll base=1;//边的权值 for(int i=1;i<=m;i++){ cin>>u>>v; base*=2; base%=mod; if(find(u)==find(v)) continue; merge(u,v); E[v].push_back(make_pair(u,base));//使用邻接表存储树的信息 E[u].push_back(make_pair(v,base)); } dfs(1,-1);//dfs求解树中每条边的贡献次数 ll sum=0; for(int i=1;i<n;i++){//求解最后的结果 sum=sum+(sum0-b[i].p0)*b[i].p1*b[i].w; sum%=mod; sum=sum+(sum1-b[i].p1)*b[i].p0*b[i].w; sum%=mod; } cout<<sum<<endl; } return 0; }
写于2020/8/7 12:16