cf补题记录

文章探讨了如何判断序列是否平衡以及计算相关操作的代价,同时涉及到了树上逆序对的期望值计算,涉及到的算法包括排列组合、动态规划和树状数组。
摘要由CSDN通过智能技术生成

E. Cost Equilibrium:

题面:

给出一个序列 a,求这个序列存在多少种排列是 balanced。
在开始时对序列中的每个元素规定其为源点还是汇点,接下来的操作中,每次可以选择 i,j,要满足 i 是源点,j 是汇点,可以选择一个任意正整数 x,使元素 i 的权值减 x,元素 j 的权值加 x,需要花费 |i−j|x 的代价。
如果对于任意的可以把序列存在至少一种规定源点汇点的方案使得对于把权值全部变为相同的任意的操作方案的代价是相同的,那么就称这个序列为 balanced。


题目分析:

假如平均数不是整数答案就为0,然后可以把数分成大于小于等于平均数的三类数。大于平均数的数作为源点,小于平均数的数作为汇点,等于就不用动,由于要是我无论怎么操作,操作次数都不变,所以我们要将汇点放在一侧,源点放在另一侧。
当汇点或源点的数量小于等于1时,我们怎么放都是可以的,其他情况我们就先放等于平均数的数,然后剩下的格子一侧放大于,一侧小于,答案是C(n,x)*2。当然,假如说数组中有相同的数,他们放的位置就会产生重复答案,比如[1,4,4]在以[1,3,2]和
[1,2,3]放置时答案会相同,所以我们要除于
A(2,2),也就是4个数的排列数。


代码:

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#pragma GCC optimize(3)
using namespace std;
const int N=1e5+5;
const int mod=1e9+7;
int n,a[N],sum,fact[N],invfact[N];
map<int,int> mp;
int qpow(int  a,int  b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
void init(){
	fact[0] = invfact[0] = 1;
	for(int i = 1; i < N; i++) fact[i] =fact[i - 1] * i % mod;
	invfact[N- 1] = qpow(fact[N - 1], mod - 2);//乘法逆元
	for(int i = N - 2; i; i--)
		invfact[i] = invfact[i + 1] * (i + 1) % mod;//分子部分	
}
int C(int a, int b){
	if (a < b) return 0;
	return fact[a] * invfact[b] % mod * invfact[a - b] % mod;
}
vector<int> v1,v2;
void solve(){
	init();
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i],sum+=a[i],mp[a[i]]++;
	if(sum%n){
		cout<<0<<endl;
		return;
	}
	sum/=n;
	int cnt1=0,cnt2=0,ans=0;
	for(int i=1;i<=n;i++){
		if(a[i]>sum)cnt1++,v1.push_back(a[i]);
		else if(a[i]<sum)cnt2++,v2.push_back(a[i]);
	}
	sort(v1.begin(),v1.end());
	sort(v2.begin(),v2.end());
	v1.erase(unique(v1.begin(),v1.end()),v1.end());
	v2.erase(unique(v2.begin(),v2.end()),v2.end());
	if(cnt1<=1||cnt2<=1)
        ans=C(n,n-cnt1-cnt2)*fact[cnt1+cnt2]%mod;
    else
		ans=C(n,n-cnt1-cnt2)*fact[cnt1]%mod*fact[cnt2]*2%mod;
	for(auto i:v1)
		ans=ans*invfact[mp[i]]%mod;
	for(auto i:v2)
		ans=ans*invfact[mp[i]]%mod;
	cout<<ans<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	solve();
	return 0;
}

D. Tree Array

题面:

给你一棵树,刚开始有相同概率感染一个节点,然后每一次都有相同概率感染与已经感染点相邻的未感染点,然后每次感染一个点直到所有点都被感染,这些点的编号会形成一个排列,为排列中逆序对个数的期望。


题目分析:

考虑枚举第一个被染色的点root,那么以root为根去遍历整棵树,枚举每对点对(u,v)计算贡献
若u是v的儿子且满足v>u,答案直接加一
若v是u的儿子且满足u>v,答案直接加一
否则求一个u,v的lca为x,那么考虑x被染色,接下来想染色u,v需要走不同的分支
设x到u需要i步,x到v需要j步
定义f[i][j]表示一条链长i,一条链长j,链i先到的概率
初始化f[0][j]=1
然后f [ i ] [ j ] = f [ i − 1 ] [ j ] ∗ 1/ 2 + f [ i ] [ j − 1 ] ∗ 1/ 2


代码:

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#pragma GCC optimize(3)
using namespace std;
const int N=205;
const int mod=1e9+7;
int n,f[N][10],d[N],dp[N][N];
vector<int> g[N];
int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
void init(){
	int inv=ksm(2ll,mod-2);
	for (int i=1;i<N;i++) dp[0][i]=1;
	for (int i=1;i<N;i++)
		for (int j=1;j<N;j++) 
			dp[i][j]=(dp[i][j-1]*inv%mod+dp[i-1][j]*inv%mod) % mod;
}
void dfs(int x,int fa){
	for(auto v:g[x]){
		if(v==fa)continue;
		f[v][0]=x;
		for(int i=1;i<=9;i++)
			f[v][i]=f[f[v][i-1]][i-1];
		d[v]=d[x]+1;
		dfs(v,x);
	}
}
int lca(int x,int y){
	if(d[x]>d[y])
		swap(x,y);
	for(int i=9;i>=0;i--){
		if(d[f[y][i]]>=d[x])
			y=f[y][i];
	}
	if(x==y)
		return x;
	for(int i=9;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			x=f[x][i],y=f[y][i];
		}
	}
	return f[x][0];
}
void solve(){
	init();
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int ans=0;
	for(int k=1;k<=n;k++){
		d[k]=1;
		for(int i=0;i<=9;i++)
			f[k][i]=0;
		dfs(k,k);	
		for(int i=1;i<=n;i++){
			for(int j=i+1;j<=n;j++){
				int x=lca(i,j);
				if(x==j)ans++;
				else if(x!=i){
					int d1=d[i]-d[x],d2=d[j]-d[x],cnt=0;
					ans=(ans+dp[d2][d1])%mod;
				}
			}
		}
	}
	ans=ans*ksm(n,mod-2)%mod;
	cout<<ans<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	solve();
	return 0;
}

E. The Three Little Pigs

题面:

多次询问∑C(3i,x)

题目分析:

在这里插入图片描述

代码:

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
#pragma GCC optimize(3)
using namespace std;
const int N=3e6+5;
const int mod=1e9+7;
int n,fact[N],invfact[N],dp[N][3],q;
int qpow(int  a,int  b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}
void init(){
	fact[0] = invfact[0] = 1;
	for(int i = 1; i < N; i++) fact[i] =fact[i - 1] * i % mod;
	invfact[N- 1] = qpow(fact[N - 1], mod - 2);//乘法逆元
	for(int i = N - 2; i; i--)
		invfact[i] = invfact[i + 1] * (i + 1) % mod;//分子部分	
}
inline int C(int a, int b){
	if (a < b) return 0;
	return fact[a] * invfact[b] % mod * invfact[a - b] % mod;
}
void solve(){
	init();
	cin>>n>>q;
	dp[0][0]=dp[0][1]=dp[0][2]=n;
	int div=qpow(3,mod-2);
	for(int i=1;i<=3*n-1;i++){
		dp[i][0]=((C(3*n,i+1)-2*dp[i-1][0]-dp[i-1][1])%mod+mod)%mod*div%mod;
	    dp[i][1]=(dp[i][0]+dp[i-1][0])%mod;
		dp[i][2]=(dp[i][1]+dp[i-1][1])%mod;
	}
	while(q--){
		int x;
		cin>>x;
		cout<<(dp[x][0]+C(3*n,x))%mod<<endl;
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
		solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值