2020杭电多校第六场1006

题意:给你n个点,每个点被标记为0或者1,边权与给定的输入顺序有关。输入的第i条边的权值为2^{i}。求所有0和1的点之间的最短路径之和。
思路:2^{1}+2^{2}+...+2^{i-1}<2^{i},构建一个最小生成树,求最小生成树之内的所有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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43916777/article/details/107860444
今日推荐