Codeforces Round #565 (Div. 3) (D+F)

心得

ABCE四个水题,

D在归神代码的思路指导下过了,觉得D这代码绝了……

F和归神糊了糊,开始想过贪心想过dp想过背包,

最后一人提了对牌数dp,一个提出用枚举6种情况代替背包

就把思路凑出来了,然而没时间写了所以补一下……

应该是期末之前最后一次打cf了,这两天再看看大创看看期末就去考试了……

两周之后见.jpg

D.Recover it!

a数组中有n(n<=2e5)个数,第i个数是ai(2<=ai<=2e5),

令b数组初始等于a数组,之后对于1到n位置的每个数进行如下操作,

①如果ai是质数,那么把质数表里第ai个质数加入b数组内

②如果ai不是质数,那么把ai的最大因子加入b数组内

现在给你长度为2*n且打乱过的b数组,让你输出一种a数组方案,题目保证有解

 

考虑3 5 6 10这组样例,如果预取3和5的话,就找不到解了,

所以如果取两个质数,可能会浪费两个合数,

而如果降序取一个合数和它的最大因子,最多浪费一个质数,

所以优先降序取合数,降序是因为最大因子的过程不可逆,

10和25都对应5,但是5不知道序列里它应该对应比它大的是谁

 

取完合数和它的最大因子之后,再成对枚举质数即可

由于剩余的质数如果可组则两两组成一对,不会产生一个质数与多个质数对应的情形

所以这个增序和降序应该是不影响的

只是归神的代码没有加入2e5以上的数,

所以只能把没加进来的质数的优先级降到最低,也就是增序取

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5;
const int maxm=3e6;
bool ok[maxm];
int num[maxm],v;
int ans[maxm];//存最小的质因子 
int a[maxn+5],tot; 
int p[maxn+5],cnt;
int n,now,nex;
vector<int>yes,no;
void sieve()
{
	for(int i=2;i<maxm;++i)
	{
		if(!ok[i])p[++cnt]=i;
		if(cnt==maxn-1)break; 
		for(int j=1;j<=cnt;++j)
		{
			ll tmp=i,k=p[j]*tmp;
			if(k>=maxm)break;
			ok[k]=1;ans[k]=p[j];
			if(i%p[j]==0)break;
		}
	}
}
int main()
{
	sieve();
	scanf("%d",&n);
	for(int i=1;i<=2*n;++i)
	{
		scanf("%d",&v);
		if(v>2e5)continue;
		if(!ok[v])yes.push_back(v);
		else no.push_back(v); 
		num[v]++;
	}
	sort(no.begin(),no.end(),greater<int>());
	for(int i=0;i<no.size();++i)//大数必须先判 不同大数对应一个最大因数 所以从大到小 
	{
		now=no[i];nex=now/ans[now];
		if(num[now]&&num[nex])
		{
			a[++tot]=now;
			num[now]--;
			num[nex]--;
		}
	}
	sort(yes.begin(),yes.end());//不存在的质数必须后判 不然不能确定留到最后 
	for(int i=0;i<yes.size();++i)
	{
		now=yes[i];nex=p[now];
		if(!num[now])continue;
		if(nex>2e5)
		{
			a[++tot]=now;
			num[now]--;
		}
		else if(num[nex])
		{
			a[++tot]=now; 
			num[now]--;
			num[nex]--;
		}
	}
	for(int i=1;i<=tot;++i)
	printf("%d%c",a[i],i==tot?'\n':' ');
	return 0;
}

F.Destroy it!

n(1<=n<=2e5)轮比赛,第i轮比赛给你ki(1<=ki<=2e5)张牌,保证这n轮牌数之和不超过2e5

当轮的牌只能当轮用,每张牌有两个权值c(1<=c<=3)代表cost,d(1<=d<=1e9)代表damage

你可以选择不超过cost总和不超过3的牌集,打出这些牌的总伤害,

特别地,当你打出第10的倍数张牌的时候,第10的倍数的那张牌的伤害乘2

求总伤害的最大值

 

dp[i][j]表示i轮打出j张牌的最大伤害,

由于只有dp[i]和dp[i-1]存在递推关系,所以第一维滚动数组优化到dp[2]

由于只有10的倍数会起作用,所以第二维只需要开dp[10]即可,

j%10是等价的,不妨认为每打10张牌历史记录清空一次,从合法状态往后递推即可

 

每轮的牌的选法,只可能是选cost为0,1,2,3,1+1,1+2,1+1+1的七种选法

所以每轮读入牌的时候,维护c=1的top-3大值,c=2和c=3的最大值即可

每一轮七次dp转移,特判10的倍数,注意滚动数组和最大值的初始化

注意,只要状态经过10的倍数就应该考虑第10-th张牌,比如9到2的转移(1+1+1)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e15;//必须有(-INF+最大值)<0 不然会给本为-1的地方产生正值 
ll dp[2][10],ans;
ll one[3],two,thr,d;
int n,k,c;
int now,last;
int to;
int main()
{
	scanf("%d",&n);
	now=0;last=1;
	memset(dp,-1,sizeof dp);
	dp[now][0]=0;
	for(int i=1;i<=n;++i)
	{
		swap(now,last);
		for(int j=0;j<=9;++j)
		dp[now][j]=-1;
		for(int j=0;j<3;++j)
		one[j]=-INF;
		two=thr=-INF;
		scanf("%d",&k);
		for(int j=1;j<=k;++j)
		{
			scanf("%d%I64d",&c,&d);
			if(c==2)two=max(two,d);
			else if(c==3)thr=max(thr,d);
			else if(c==1)
			{
				if(d>one[0])one[2]=one[1],one[1]=one[0],one[0]=d;
				else if(d>one[1])one[2]=one[1],one[1]=d;
				else if(d>one[2])one[2]=d;
			}
		}
		//for(int j=0;j<3;++j)
		//printf("one[%d]:%I64d\n",j,one[j]);
		//printf("two:%I64d thr:%I64d\n",two,thr);
		//1 2 3 1+1 1+2 1+1+1 
		//1
		for(int j=0;j<=9;++j)
		if(~dp[last][j])dp[now][j]=dp[last][j];
		for(int j=0;j<=9;++j)
		{
			to=(j+1)%10;
			if(~dp[last][j])
			{
				if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]);
				else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]);
			}
		}
		//2
		for(int j=0;j<=9;++j)
		{
			to=(j+1)%10;
			if(~dp[last][j])
			{
				if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*two);
				else dp[now][to]=max(dp[now][to],dp[last][j]+two);
			}
		}
		//3 
		for(int j=0;j<=9;++j)
		{
			to=(j+1)%10;
			if(~dp[last][j])
			{
				if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*thr);
				else dp[now][to]=max(dp[now][to],dp[last][j]+thr);
			}
		}
		//第10-th牌,不该是等于0,而应该是经过0 
		//1+1
		for(int j=0;j<=9;++j)
		{
			to=(j+2)%10;
			if(~dp[last][j])
			{
				if(j+2>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]+one[1]);
				else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+one[1]);
			}
		}
		//1+2
		for(int j=0;j<=9;++j)
		{
			to=(j+2)%10;
			if(~dp[last][j])
			{
				if(j+2>=10)dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+two+max(one[0],two));
				else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+two);
			}
		}
		//1+1+1
		for(int j=0;j<=9;++j)
		{
			to=(j+3)%10;
			if(~dp[last][j])
			{
				if(j+3>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]+one[1]+one[2]);
				else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+one[1]+one[2]);
			}
		}
		//for(int j=0;j<=9;++j)
		//printf("dp[%d]:%I64d\n",j,dp[now][j]);
	}
	for(int j=0;j<=9;++j)
	ans=max(ans,dp[now][j]);
	printf("%I64d\n",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值