Codeforces Round #652(Div.2, CF1369A)A-D题解

花絮: 本场比赛考前押了前四题的算法,结果全部押对

开题顺序: CADBFE

A

Solution

显然,只有当 n n n 4 4 4的倍数时才满足要求,否则不满足要求。

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int t,n;
 
signed main()
{
	cin>>t;
	while (t--)
	{
		cin>>n;
		if (n%4==0)  cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

B

Solution

首先,前缀 0 0 0不可能被删去的;同理,后缀 1 1 1也是不可能被删去的。我们把这些东西留下,剩下的一定是 111 … 1000 … 0111 … 1000 … 111…1000…0111…1000… 111100001111000的形式。

考虑对于每块 111 … 000 … 111…000… 111000最后都可以化为一个数( 0 0 0 1 1 1)。原因显然,我们先用最后一个 1 1 1删去许多 0 0 0,只留下最后一个 0 0 0;然后再用这个 0 0 0删去所有的 1 1 1,就留下了 0 0 0。当然,我们也可以先用第一个 0 0 0删去 1 1 1(留下一个 1 1 1),然后再用这个 1 1 1删去 0 0 0,就留下了 1 1 1

假设有 k k k块这样的 111 … … 1000 … … 0 111……1000……0 11110000,那么相当于我们可以在这 k k k块中任意选择最终留下的数字,并继续在这 k k k块中进行删除操作。那么,在这 k k k块中,我们可以使得最终留下一个 0 0 0,即最优解——对于第一个块我们选择留下 1 1 1,后面的所有块都选择留下 0 0 0,那么在 1000 … … 0 1000……0 10000中进行删除操作我们可以得到仅仅一个 0 0 0

当然,如果根本没有这样的块,即该字符串只有前缀 0 0 0与后缀 1 1 1,那么中间当然什么也不会留下。否则中间就会留下一个 0 0 0

时间复杂度 O ( n ) O(n) O(n)

方法貌似复杂了QWQ

Code

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

int t,n;
int a[200005];

signed main()
{
	cin>>t;
	while (t--)
	{
		cin>>n;
		for (int i=1;i<=n;i++)
		{
			char x;
			cin>>x;
			a[i]=x-'0';
		}
		int l=n,r=1;
		for (int i=1;i<=n;i++)
		{
			if (a[i]==1)
			{
				l=i-1;
				break;
			}
		}
		for (int i=n;i>=1;i--)
		{
			if (a[i]==0)
			{
				r=i+1;
				break;
			}
		}
		for (int i=1;i<=l;i++)  cout<<0;
		if (l+1<r)  cout<<0;
		for (int i=r;i<=n;i++)  cout<<1;
		
		cout<<endl;
	}
	return 0;
}

C

Solution

首先,思考一种情况:如果一个Lee的朋友只需要一个礼物,那么应该贪心地把目前留下的礼物中最大的礼物给她。

先把只需要一个礼物的朋友处理好。接着,我们开始做核心步骤:

①把这些礼物从小到大排序;
②把这些朋友按照礼物需求的多少降序排序。


之所以我们要按降序排序,是因为:对于每一些被选择给同一个朋友的礼物中,凡不是最小或最大值的都是被浪费的。而且我们无法保证避免这种情况,所以我们尝试减少浪费。即,前期给需求量大的朋友礼物,浪费了一大堆小礼物;而后期的话,许多礼物都被选了,留下了中间的一堆礼物,那么需求量大的朋友就浪费了一大堆中型礼物。显然前者更划算,所以要按降序排序。


维护两个值 l l l, r r r,即目前未被选到的左端点与右端点。同理,我们需要让浪费的少,那么我们每次选取剩下的礼物中最大的(1个),与剩下的礼物中小的,使得礼物量足够。此时浪费的是许多小的,总比浪费大的好吧。

然后每次统计一下每个朋友收到的礼物中最大的以及最小的之和,然后累加一下即可。

时间复杂度: O ( n ) O(n) O(n)

方法貌似复杂了QWQ

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int t,n,k;
int a[200005],b[200005];
 
bool cmp(int x,int y)
{
	return x>y;
}
 
signed main()
{
	cin>>t;
	while (t--)
	{
		cin>>n>>k;
		for (int i=1;i<=n;i++)  cin>>a[i];
		for (int i=1;i<=k;i++)  cin>>b[i];
		
		sort(a+1,a+n+1);
		sort(b+1,b+k+1,cmp);
		
		int l=1,r=n,ans=0;
		for (int i=1;i<=k;i++)
		{
			if (b[i]==1)
			{
				ans+=a[r]*2;
				r--;
			}
		}
		for (int i=1;i<=k;i++)
		{
			if (b[i]==1)  break;
			
			int minv=1e9+7,maxv=a[r];
			for (int j=l;j<=l+b[i]-2;j++)  minv=min(minv,a[j]);
			
			ans+=minv+maxv;
			l=l+b[i]-1;
			r--;
		}
		cout<<ans<<endl;
	}
	return 0;
} 

D

较为简单的 d p dp dp与较为简单的前缀和与较为简单的数学与思维复杂度,造就了这道有一定难度的综合题。

Solution

状态设计 d p i dp_i dpi表示第 i i i轮长出来的节点。
状态转移就是模拟。即,第 i − 1 i-1 i1轮长出来的节点之前没有孩子,现在多了一个孩子;第 i − 2 i-2 i2轮长出来的节点在第 i − 1 i-1 i1轮长出来一个孩子,第 i i i轮会长出来 2 2 2个孩子;即状态转移为 d p i = d p i − 1 + 2 d p i − 2 dp_i=dp_{i-1}+2dp_{i-2} dpi=dpi1+2dpi2


考虑答案是什么。

首先,我们需要贪心地先选最深层的 c l a w claw claw;之所以不选浅层的 c l a w claw claw,是因为它会占据掉一些关键的节点,一些 c l a w claw claw无法形成,使得它不是最优解。

首先,根据前面说的东西,在第 i − 2 i-2 i2轮长出来的节点已经成为了许多可爱的 c l a w claw claw的根啦!而第 i − 3 i-3 i3, i − 4 i-4 i4长出来的节点,以其根的 c l a w claw claw已经被上面所说的 c l a w claw claw给占据了一部分。那么,我们只能选择再前 3 3 3轮,即第 i − 5 i-5 i5轮长出来的节点;以其为根形成许多 c l a w claw claw。同理,第 i − 8 i-8 i8轮长出来的节点为根形成许多 c l a w claw claw,第 i − 11 i-11 i11轮,第 i − 14 i-14 i14轮……

综上所述答案就是 d p i − 2 + d p i − 5 + d p i − 8 + … dp_{i-2}+dp_{i-5}+dp_{i-8}+… dpi2+dpi5+dpi8+。预处理出 p r e pre pre数组,并预处理前缀和数组进行优化,可以使得每次询问的时间复杂度均为 O ( 1 ) O(1) O(1)

另外,记得特判 n ≤ 2 n≤2 n2的情况,否则会RE。

总时间复杂度: O ( m a x   n + q ) O(max\ n+q) O(max n+q)

Code

#include <bits/stdc++.h>
#define int long long
#define maxlen 2000000
using namespace std;
const int mod=1e9+7;
 
int t,n;
int a[2000005],pre[2000005];
 
inline void init()
{
	a[1]=1,a[2]=1;
	pre[1]=1,pre[2]=1;
	for (int i=3;i<=maxlen;i++)  a[i]=(a[i-1]+2*a[i-2])%mod;
	for (int i=3;i<=maxlen;i++)  pre[i]=(pre[i-3]+a[i])%mod;
}
 
signed main()
{
	cin>>t;
	init();
	
	while (t--)
	{
		cin>>n;
		
		if (n<=2)  cout<<0<<endl;
		else cout<<(4*pre[n-2])%mod<<endl;
	}
	return 0;
}

虽然AC了A-D题,但是提交页面惨不忍睹。

①C题WA
②C题WA
③C题WA(心态爆炸,放弃)
④A题CE(语言选错,比赛的时候根本不知道,自己已经奔溃)
⑤A题AC(猛然发现语言的问题,改成C++后AC) 😃
⑥D题RE(没有特判 n ≤ 2 n≤2 n2的情况)
⑦D题WA(太着急了,把调试内容也提交上去了)
⑧D题AC 😃

回来看B题做不出来,于是回到C题
⑨C题AC(换了个似乎可行的贪心策略,果断交) 😃
⑩B题WA(前缀 0 0 0与后缀 1 1 1不会找了;事实上当时已经放平心态,毕竟已经AC ACD大约满足了)
⑪B题AC(改了一番就过了) 😃

呜呜呜, 5 5 5次啊,被扣了 250 250 250分, P e n a l t y Penalty Penalty爆涨……

菜死了QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值