CF #638(div.2)

Posted 17134h

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF #638(div.2)相关的知识,希望对你有一定的参考价值。

凤凰专场

@

A. Phoenix and Balance

题意: 有 n 个硬币分别重 (2^1,2^2,...,2^n~(n in even)) , 将这 n 个硬币分成相同数量的两组,求两组重量差值最小是多少

分析: 硬币的重量是 2 的幂次增长,所以前 n-1 个硬币的重量和是 小于第 n 个硬币的,为了使分组后的重量差值最小,所以肯定是选择 最后一个硬币和前 (frac{n}{2}-1) 为一组,其余的为一组

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	ll a[35];
	a[0]=0,a[1]=2;
	for(int i=2;i<=30;i++) a[i]=a[i-1]*2;
	for(int i=2;i<=30;i++) a[i]+=a[i-1];
	int t,n; cin>>t;
	while(t--)
	{
		cin>>n;
		cout<<a[n/2-1]+a[n]-a[n-1]-(a[n-1]-a[n/2-1])<<‘
‘;
	} 
} 

B. Phoenix and Beauty

题意: 先给定 n 和 k ,((1 leq k leq n leq 100)),接着给出大小为 n 的数组 a ,((1 leq a_i leq n)) ,你可以多次在 a 数组内任意位置插入 1 ~ n 的任意数字,要求最终的 a 数组满足任意 k 个连续的数之和都相等,设最终数组的大小为 m ,需满足 ((n leq m leq 10^4)) ,无解则输出 -1 ,有解输出最后的 a 数组

分析: 首先考虑无解的情况,明显的如果原 a 数组中出现的不同数字的数量 s 大于 k ,则一定无解,再看数据范围 (n imes k leq 10^4) ,所以在原 a 数组中出现的不同数字的数量 s 小于等于 k 的情况下,可以构造出长度为 k 的序列 (原数组 s 个不同的数字,然后补充 k-s 个相同的数字(1~n任选) ) ,将这个序列重复 n 次,这样既满足任意连续 k 个数的和相同,同时也满足可以从中找到原数组 a 的序列

代码:

a#include<bits/stdc++.h>
using namespace std;
const int N = 1E4+10;
bool vis[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t,n,k;
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		for(int i=0;i<=n;i++) vis[i]=false;
		int cnt=0; vector<int>G;
		for(int i=0,x;i<n;i++)
		{
			cin>>x;
			if(!vis[x]) cnt++,vis[x]=true,G.push_back(x); 
			//出现不同数字统计
		} 
		if(k<cnt) {cout<<"-1
";continue;} //无解的情况
		cout<<n*k<<‘
‘;
		for(int i=0;i<n;i++)
		{
			for(auto v:G) cout<<v<<‘ ‘;
			for(int j=0;j<k-cnt;j++) cout<<"1 ";  //不足补充 1 
		}
		cout<<‘
‘;	
	}
} 

C. Phoenix and Distribution

题意: 给定 n 和 k ((1 leq k leq n leq 10^5)) ,再给出长度为 n 的字符串 s ,现将 s 中的字符分成 k 组,每个字符任意分配,但需要保证每个组内至少有一个字符,且每个组内的字符可以任意排列,求 k 组内的字典序最大组最小

分析: 先把字符串 s 按字典序排序,如果前 k 个字符不相等,那么肯定是直接输出第 k 个字符就是了,否则每组的第一位肯定是这前 k 个相同的字符,这样每组都至少有一个字符了;再判断后面的 n-k 个字符是否相等,若相等则均分 k 组;若不等则为了字典序最大最小,那么这 n-k 个字符加上前面的第一位字符即答案

代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t,n,k;
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		string s; cin>>s;
		sort(s.begin(),s.end());
		if(s[0]!=s[k-1]) cout<<s[k-1];
		else if(s[k]==s[n-1]) {cout<<s[0];for(int i=0;i<(n-k+(k-1))/k;i++) cout<<s[k];}
		else {for(int i=k-1;i<n;i++) cout<<s[i];}
		cout<<‘
‘;
	}
} 

D. Phoenix and Science

题意: 单个细菌白天可以分裂成两个细菌,而晚上每个细菌的质量都会增大 1 个单位,第一天开始仅有一个质量为 1 的细菌,现在你可以控制每个细菌每天是否分裂,给定 n,问是否可以经过若干天,细菌总质量恰好达到 n,求这个最小天数并给出分裂具体方案

分析: 质量最大的增速是每天所有的细菌都分裂,这样质量的增长就是 (2^0,2^1,2^2,...) 所以后一天质量的增量不超过前一天的一倍,令 (sum_i=2^0+2^1+2^2+...+2^i) ,则找到 k 使得 (sum_k leq n < sum_{k+1}) ,那么我们最少都需要 k+1 天,再令 (left=n-sum_k) ,再找到 m 使得 (sum_m leq left < sum_m+1) ,那么可以把 left 插进去,最终序列依旧满足分裂的要求,总共需要 k+1 天,满足天数最小的要求

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
ll a[35],sum[35];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	a[0]=1;
	a[1]=2;
	for(int i=2;i<=30;i++) a[i]=2*a[i-1];
	for(int i=1;i<=30;i++) sum[i]=sum[i-1]+a[i];
	ll t,n;
	cin>>t;
	while(t--)
	{
		vector<ll>G; G.pb(1);
		cin>>n; n--;

		int tim=1;
		while(sum[tim]<=n) tim++;
		tim--;
		for(int i=1;i<=tim;i++) G.pb(a[i]);if(n-sum[tim]>0) G.pb(n-sum[tim]);
		sort(G.begin(),G.end());
		cout<<(int)G.size()-1<<‘
‘;
		for(int i=1;i<G.size();i++) cout<<G[i]-G[i-1]<<‘ ‘;
		cout<<‘
‘;
	}
} 

E. Phoenix and Berries

题意: 有 n 棵果树,第 i 棵果树有 (a_i) 个红果实 和 (b_i) 个蓝果实,现在要求把这些果实装到篮子中,每个篮子的容量为 k,且一个篮子内的果实要么都属于同一种颜色,否则都必须属于同一棵果树,问这 n 棵果实最多可以装满多少个篮子

分析: 我们设第 i 棵果树最多可以装满

  • 一类:A 个只含红果实的篮子
  • 二类:B 个只含蓝果实的篮子
  • 三类:C 个同时含有两种果实的篮子

若 C>1 ,则任意两个 C 篮子合并,红果实或者蓝果实必有一方数量大于等于 k ,相当于 ((A+1,C-1)) 或者 ((B+1,C-1)),所以可以设定 (C leq 1),那么令 (dp[i][j]) 表示前 i 个果树剩下 j 个红果实的情况下,能装满最多的篮子数,我们给果树做前缀和 sum , 则 (dp[ i ][ j ]) 表示剩下的蓝果实数量即 (blue=sum_i-dp[i][j]*k-j),接下来枚举第 i+1 棵树的果实装满 (C~(C leq 1)) 个第三类篮子的情况 DP 一下就 OK 了

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 500+10
ll dp[N][N],sum;
int n,k,a[N],b[N],c[N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
	
    for(int i=0;i<=n;i++)
	  for(int j=0;j<=k;j++)
	    dp[i][j]=-1; 
	    
	dp[0][0]=sum=0;
	for(int i=1;i<=n;i++)
	{
	    for(int j=0;j<k;j++)
		{
			if(dp[i-1][j]==-1) continue;
			ll now=sum-dp[i-1][j]*k;
			ll red=j,blue=now-j;  
			//对于dp[i-1][j]:前 i-1 棵树留下的红果实red和蓝果实blue数量
			
			//z枚举的是当前C=1情况下第三类篮子需要红果实的数量
			for(ll z=1;z<=min(k-1,a[i]);z++)
			{
				if(b[i]+z<k) continue;  //如果第i棵树的所有蓝果实+z<k,则凑不满一篮子
				
				ll nred=red+a[i]-z;     //装满这一个第三类篮子后,累加剩下的红果实和蓝果实
				ll nblue=blue+b[i]-(k-z);
				
				ll res=dp[i-1][j]+1+nred/k+nblue/k;
				dp[i][nred%k]=max(res,dp[i][nred%k]);
			}
			//这一步是枚举当C=0的情况
			dp[i][(red+a[i])%k]=max(dp[i-1][j]+(red+a[i])/k+(blue+b[i])/k,dp[i][(red+a[i])%k]);
		} 
		sum+=a[i]+b[i];  //维护果树前缀和 
	}
	ll ans=0;
	for(int i=0;i<=k;i++)
	    ans=max(ans,dp[n][i]);
	cout<<ans;
}

F. Phoenix and Memory

题意: 给定 n 个二元组 ((l_i,r_i)) ,问是否存在唯一的 1~n 的序列 s 满足 (l_i leq s_i leq r_i),若唯一则输出 YES 和这个序列,否则输出 NO 和任意两种合法的序列

分析: 只求一种序列的话贪心就好,把二元组按照 r 从小到大排序,每次尽量选小的;若存在多解,那么一个解必定是由另一个解的两个位置互换而成,所以我们贪心求得第一个解后,考虑在第一个解的序列上交换两个位置得到第二个解,我们假设数 i 和 j ((i<j))可以互相交换,且它们所在位置分别为 (k_i)(k_j) ,则需要满足

  • (l[k_j] leq i < j leq r[k_i])

所以可以线段树维护每个数所在位置的左边界,然后查询的时候枚举 i ,查询区间 ([i+1,r[i]]) 内的数的最左边界是否小于等于 i 即可,细节挺多,需要仔细一些

代码:

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define sd second
#define  P pair<int,int>
const int N = 2E5+10;
const int INF = 1e8;

struct node{
	int l,r,id;
	bool operator < (const node& a)const{
	    return r<a.r;
	}
}e[N];

set<int>s;

int n,ANS[N],SET[N],OP[N];
P p[N<<2];

void build(int l,int r,int rt)
{
	if(l==r){
		p[rt]=P(l,INF);
		return;
	}
	int m=(l+r)>>1;
	build(l,m,rt<<1);
	build(m+1,r,rt<<1|1);
	p[rt]=P(l,INF);
}

void update(int l,int r,int rt,int x,int lft)
{
	if(l==r){
		p[rt]=P(x,lft);
		return;
	}
	int m=(l+r)>>1;
	if(x<=m) update(l,m,rt<<1,x,lft);
	else update(m+1,r,rt<<1|1,x,lft);
	
	if(p[rt<<1].sd<p[rt<<1|1].sd) p[rt]=p[rt<<1];
	else p[rt]=p[rt<<1|1];
}

P query(int l,int r,int rt,int L,int R)
{
	if(L<=l&&r<=R) return p[rt];
	int m=(l+r)>>1;
	P p1=P(0,INF),p2=P(0,INF);
	if(L<=m) p1=query(l,m,rt<<1,L,R);
	if(R>m) p2=query(m+1,r,rt<<1|1,L,R);
	
	if(p1.sd<p2.sd) return p1;
	else return p2;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>e[i].l>>e[i].r;
		e[i].id=i;
		s.insert(i);
	}
	build(1,n,1);
	sort(e+1,e+n+1);
	
	for(int i=1;i<=n;i++)
	{
		auto it=s.lower_bound(e[i].l);
		ANS[e[i].id]=*it;
		SET[*it]=e[i].id;
		OP[i]=*it;
		update(1,n,1,*it,e[i].l);
		s.erase(it);
	}
	
	for(int i=1;i<=n;i++)
	{
		
		P pp=query(1,n,1,OP[i]+1,e[i].r);
		if(pp.sd<=OP[i])
		{
			cout<<"NO
";
			for(int j=1;j<=n;j++)
			    cout<<ANS[j]<<‘ ‘;
			    cout<<‘
‘;
			swap(ANS[e[i].id],ANS[SET[pp.fi]]); 
			for(int j=1;j<=n;j++)
			    cout<<ANS[j]<<‘ ‘;
			return 0;
		}
	}
	cout<<"YES
";
	for(int i=1;i<=n;i++) cout<<ANS[i]<<‘ ‘;
} 

以上是关于CF #638(div.2)的主要内容,如果未能解决你的问题,请参考以下文章

CF A. Phoenix and Balance Codeforces Round #638 (Div. 2) 5月1号

# $CF 638 (Div2)$

Codeforces Round #638 (Div. 2)

Codeforces Round #638 (Div. 2)

SRM 638 Div.2

Topcoder SRM 638 DIV 2 (大力出奇迹)