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; }