1. 程式人生 > 實用技巧 >A Very Easy Graph Problem

A Very Easy Graph Problem

A Very Easy Graph Problem

題解:首先根據\(2^{i}\)的特殊性,我們可以發現最短路其實就是最小生成樹上的路,那麼我們就可以先把圖換成最小生成樹;然後我們看一條邊要經過幾次,就是要看一條邊對答案的貢獻:某一條邊被遍歷的次數必定是這條邊下面的所有權值1的點的個數 * 這條邊上面所有權值0的點的個數 + 下面所有權值0的點的個數 * 上面所有權值1的點的個數(這裡我們自定義一個根來看),那麼就是要\(dfs\)進行統計了:統計每個點以及它所有子樹的權值為1的點的個數和以及權值0的點的個數和。

AC_Code:

#include <bits/stdc++.h>
using
namespace std; typedef long long ll; #define endl '\n' const int mod=1e9+7; const int maxn = 2e5+10; vector<ll>G[maxn]; ll w[maxn]; ll a[maxn],u[maxn],v[maxn]; ll fa[maxn],dfn[maxn]; bool is[maxn]; ll num[maxn][2],cnt,sum[2]; ll find_fa(ll u){ if( fa[u]==u ) return u; return fa[u]=find_fa(fa[u]); }
void dfs(ll now,ll fa){ dfn[now]=++cnt; num[now][0] = (a[now]==0); num[now][1] = (a[now]==1); for(size_t i=0;i<G[now].size();i++){ ll to=G[now][i]; if( to==fa ) continue; dfs(to,now); num[now][0] += num[to][0]; num[now][1] += num[to][1]; } } ll qpow(ll a,ll b){ ll res
=1; while(b){ if( b&1 ) res=res*a%mod; a=a*a%mod; b>>=1; } return res; } int main() { for(ll i=0;i<maxn;i++){ w[i]=i; } ll t; cin>>t; while( t-- ){ ll n,m; cin>>n>>m; sum[0]=sum[1]=0; for(ll i=1;i<=n;i++){ cin>>a[i]; sum[0] += (a[i]==0); sum[1] += (a[i]==1); fa[i]=i; G[i].clear(); } memset(is,false,sizeof(is)); for(ll i=1;i<=m;i++){ cin>>u[i]>>v[i]; ll fu=find_fa(u[i]); ll fv=find_fa(v[i]); if( fu==fv ) continue; fa[fv]=fu; is[i]=true; G[u[i]].push_back(v[i]); G[v[i]].push_back(u[i]); } cnt=0; dfs(1,0); ll ans=0; for(ll i=1;i<=m;i++){ if( !is[i] ) continue; ll fa=u[i], son=v[i]; if( dfn[fa]>dfn[son] ) swap(fa,son); ll fa0 = sum[0]-num[son][0]; ll fa1 = sum[1]-num[son][1]; ans = (ans + qpow(2,w[i])*(fa0*num[son][1]%mod)%mod)%mod; ans = (ans + qpow(2,w[i])*(fa1*num[son][0]%mod)%mod)%mod; } cout<<ans<<endl; } return 0; }