当前位置: 代码迷 >> 综合 >> 2020杭电多校第六场1006
  详细解决方案

2020杭电多校第六场1006

热度:43   发布时间:2024-02-07 19:50:44.0

题意:给你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;
}