题意:给你n个点,每个点被标记为0或者1,边权与给定的输入顺序有关。输入的第i条边的权值为。求所有0和1的点之间的最短路径之和。
思路:,构建一个最小生成树,求最小生成树之内的所有0和1的点最短路径之和即可。只需要算每条边两侧的01对即可。显然枚举每条边爆搜会超时,通过树形dp可以压缩复杂度。dp[i][j]表示第i个点及之后的标记为j的点有几个。那么这条边的两侧的01对就可以计算出来,比如某个点u,它之后的标记为1的点有x个,那么,在这条边的另一侧,标记为0的点有(总的标记为0的点数-u及之后标记为0的点数)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2e5+5;
const ll mod=1e9+7;
ll T,n,m,u,v,sum,a[N],fa[N],dp[N][2];
vector<pair<ll,ll> > G[N];
unordered_map<ll,ll> mp;
inline ll find(ll x){return x==fa[x]?fa[x]:(fa[x]=find(fa[x]));}
inline void dfs(ll x,ll p){
dp[x][0]=dp[x][1]=0;
dp[x][a[x]]++;
for(auto it:G[x]){
ll v=it.first;
if(v==p) continue;
dfs(v,x);
dp[x][0]+=dp[v][0];
dp[x][1]+=dp[v][1];
}
for(auto it:G[x]){
ll v=it.first,w=it.second;
if(v==p) continue;
sum=(sum+dp[v][0]*(mp[1]-dp[v][1])%mod*w)%mod;
sum=(sum+dp[v][1]*(mp[0]-dp[v][0])%mod*w)%mod;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
cin>>n>>m;mp.clear();sum=0;
for(ll i=1;i<=n;i++) cin>>a[i],mp[a[i]]++,fa[i]=i,dp[i][0]=dp[i][1]=0,G[i].clear();
ll ans=1;
for(ll i=1;i<=m;i++){
cin>>u>>v;ans=ans*2%mod;
ll tu=find(u),tv=find(v);
if(tu!=tv) G[u].push_back({v,ans}),G[v].push_back({u,ans}),fa[tu]=tv;
}
dfs(1,0);
cout<<(sum+mod)%mod<<endl;
}
return 0;
}