Loading

Codeforces Round #774 (Div. 2)

A

有点诈骗的感觉,其实可以发现 \(n^2\)\(n\) 根本不在同一个数量级上,所以答案就是 \(\dfrac{s}{n^2}\)

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
void solve(){
	int n,s;
	cin>>n>>s;
	cout<<s/n/n<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)
		solve();
	return 0;
}

B

题意比较绕,所以写慢了。考虑红色的和要大于蓝色但是红色的个数要小于蓝色。那么红色肯定是取大的数,蓝色一定是取小的数。贪心地构造,对数组排个序,然后红色从后往前取,蓝色从前往后取,并且红色的个数比蓝色少一,然后一格一格向中间并拢,容易证明一定是最优的策略。

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
int a[MAXN];
void solve(){
	int n;cin>>n;
	rep(i,1,n) cin>>a[i];
	sort(a+1,a+1+n);
	int mid=(2+n)>>1;
	if(n%2==0) mid--;
	int sr=0,sb=a[1];
	rep(i,2,mid){
		sb+=a[i],sr+=a[n-i+2];
		if(sr>sb){
			cout<<"YES\n";
			return;
		}
	}cout<<"NO\n";
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)
		solve();
	return 0;
}

C

首先如果去掉用阶乘来表示,那么容易想到 \(popcount\) 就是这个数用二进制最少的表示方法。加上阶乘呢?可以发现,不超过 \(10^{12}\) 的阶乘数是不超过 \(15\) 个的。于是我们 \(2^{15}\) 暴力枚举每个阶乘选不选,然后减去这个阶乘后就求它的 \(popcount\) 作为当前答案。

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
int fac[16];
int popcnt(int x){
	int ret=0;
	while(x) ret+=(x&1),x>>=1;
	return ret;
}
void solve(){
	int n,ans=INF;cin>>n;
	rep(i,0,(1<<15)-1){
		int sum=0,cnt=0;
		rep(j,0,14) if(i&(1<<j))
			sum+=fac[j],cnt++;
		if(sum>n) continue;
		ans=min(ans,popcnt(n-sum)+cnt);
	}cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	fac[0]=1;rep(i,1,15) fac[i]=fac[i-1]*i;
	int T;for(cin>>T;T--;)
		solve();
	return 0;
}

D

考虑到好点个数是第一关键字,所以先想想怎么求出最多的好点数。很容易想到一点就是除去 \(n=2\) 的情况,相邻的两个点不可能同时为好点,因为如果说 \(u,v\) 同时是好点,那么我们令和 \(u\) 相邻的不是 \(v\) 的节点权值和为 \(sum_u\),同理命 \(sum_v\)。那么就有 \(val_v+sum_u=val_u\)\(val_u+sum_v=val_v\)。那么这个等式成立当且仅当 \(sum_v=sum_u=0\)

这个限制就很像没有上司的舞会了,那我们直接跑树形 dp。

然后考虑构造这个最小的权值和。首先如果不是好点权值一定是 \(1\),那么好点的权值就是与之相邻的点数。既然要考虑到这个权值,那么就产生了一个问题,如果树形 dp 的时候,儿子是不是好点所得的好点数相同的话,那么还要进一步判断它的权值和,取小的那个。

于是我们令 \(dp[i][0/1]\) 表示 \(i\) 是不是好点的情况下子树中最多的好点数以及 \(sum[i][0/1]\) 表示 \(i\) 是不是好点的情况下子树中最小的权值和(在好点最多的条件下)。

直接跑一遍,然后构造一下就行了。

$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=2e5+10;
vector<int> e[MAXN];
int dp[MAXN][2],sum[MAXN][2];
vector<pii> from[MAXN][2];
void dfs(int x,int fa){
	dp[x][0]=0;dp[x][1]=1;
	sum[x][0]=1;sum[x][1]=e[x].size();
	for(int s:e[x]){
		if(s==fa) continue;
		dfs(s,x);
		dp[x][1]+=dp[s][0];
		sum[x][1]+=sum[s][0];
		from[x][1].pb(mkp(s,0));
		if(dp[s][0]>dp[s][1]){
			dp[x][0]+=dp[s][0];
			sum[x][0]+=sum[s][0];
			from[x][0].pb(mkp(s,0));
		}else if(dp[s][0]<dp[s][1]){
			dp[x][0]+=dp[s][1];
			sum[x][0]+=sum[s][1];
			from[x][0].pb(mkp(s,1));
		}else{
			if(sum[s][0]<sum[s][1]){
				dp[x][0]+=dp[s][0];
				sum[x][0]+=sum[s][0];
				from[x][0].pb(mkp(s,0));
			}else{
				dp[x][0]+=dp[s][1];
				sum[x][0]+=sum[s][1];
				from[x][0].pb(mkp(s,1));
			}
		}
	}
}
int num[MAXN];
int cfs(int x,int chs){
	int ret=(chs?e[x].size():1);
	num[x]=(chs?e[x].size():1);
	for(auto s:from[x][chs]){
		ret+=cfs(s.fi,s.se);
	}return ret;
}
int n;
void print(int chs){
	int rubbb=cfs(1,chs);
	cout<<dp[1][chs]<<' '<<rubbb<<'\n';
	rep(i,1,n) cout<<num[i]<<' ';
	cout<<'\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	rep(i,2,n){
		int u,v;cin>>u>>v;
		e[u].pb(v);e[v].pb(u);
	}
	if(n==2){
		cout<<"2 2\n";
		cout<<"1 1\n";
		return 0;
	}
	dfs(1,1);
	if(dp[1][0]>dp[1][1]) print(0);
	else if(dp[1][0]<dp[1][1]) print(1);
	else{
		if(sum[1][0]<sum[1][1]) print(0);
		else print(1);
	}
	return 0;
}

E

考虑进行一个打表。首先如果有 \(a^b=c^d\),那么一定有 \(a\)\(b\) 是同一个数的整次幂。于是我们依次考虑每个数,以 \(2\) 为例,我们把打出的表中 \(2\) 的整次幂爬出来看看。

2 4 8 16 32 ...
4 16 64 256 1024 ...
8 64 ...

用他们的次数来代表它:

1 2 3 4 5 ...
2 4 6 8 10 ...
3 6 9 ...

这不是乘法表吗。实际上无论对于任何一个数,我们把它写出来后都是这个样子的。那么两个数相等等价于其指数相等。然后我们对这张乘法表预处理一下到某一行为止,有多少个不同的数,用一个 bitset 维护一下就可以了。

最后做的时候,就直接枚举每个数,然后直接加上上面预处理出来的不同的数的个数就行了。

$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e6+10;
const int LOGB=20;
int a[MAXN];
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int n,m,ans=1;
	cin>>n>>m;
	bitset<MAXN*LOGB> ppc;
	rep(i,1,LOGB){
		rep(j,1,m) ppc[i*j]=1;
		a[i]=ppc.count();
	}
	bitset<MAXN*LOGB> vis;
	rep(i,2,n){
		if(vis[i]) continue;
		int cur=1; vis[i]=1;
		for(int j=i*i;j<=n;j*=i)
			vis[j]=1,cur++;
		ans+=a[cur];
	}cout<<ans<<'\n';
	return 0;
}

F

posted @ 2022-03-05 13:30  ZCETHAN  阅读(48)  评论(0编辑  收藏  举报