1. 程式人生 > 實用技巧 >2020hdu多校第六場1006A Very Easy Graph Problem

2020hdu多校第六場1006A Very Easy Graph Problem

題目地址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