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

本文详细解析了CodeForces竞赛中的三道题:D. Recoverit!,E. Coverit! 和 F. Destroyit!。介绍了如何使用线性筛、01染色法和动态规划等算法解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: https://codeforces.com/contest/1176
题目:
D. Recover it!
题面:
在这里插入图片描述

思路: 线性筛出199999个质数,顺便储存每个合数的最大因子,然后2750131开始倒推,大的质数肯定是某个小的数映射而来,大的合数可以求出最大因子数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int a[N];int b[2750135];
int prime[N];
bool vis[2750135];
int di[2750135];
int cntt=0;
void init()
{

	for(int i=2;i<=2750131;i++)
	{
		if(!vis[i])
			prime[++cntt]=i;
		for(int j=1;j<=cntt&&prime[j]*i<=2750131;j++)
		{
			di[i*prime[j]]=max(i,di[i*prime[j]]);
			vis[prime[j]*i]=1;
			if(i%prime[j]==0) break;
		}
	}
}
int main()
{
	init();
	int n;
	int maxx=0;
	scanf("%d",&n);
	for(int i=1;i<=2*n;i++)
	{
		int num;
		scanf("%d",&num);
		b[num]++;
		if(num>maxx)
			maxx=num;
	}
	int cnt=0;
	for(int i=maxx;i>1;i--)
	{
		while(b[i])
		{
			b[i]--;
			if(vis[i])
			{
				a[++cnt]=i;
				b[di[i]]--;
			}
			else
			{
				int tmp=lower_bound(prime+1,prime+cntt+1,i)-prime;
				a[++cnt]=tmp;
				b[tmp]--;
			}
		}

	}
	for(int i=1;i<=cnt;i++)
		printf("%d ",a[i]);
	puts("");
	return 0;
}

**E - Cover it! **
题意: 给你一个无向图,问你最多选出n/2个点,使剩下的点至少连接着某个已选的点
思路: 01染色经典问题,ans=min ( cnt[ 0 ],cnt[ 1 ] )

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
vector<int>G[N];
int vis[N];
void dfs(int u,int x)
{
	vis[u]=x;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(vis[v]==-1)
			dfs(v,x^1);

	}
}

int main()
{
	int T;
	scanf("%d",&T);
	int n=0,m;
	while(T--)
	{

		for(int i=1;i<=n;i++)
			G[i].clear();
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		for(int i=1;i<=n;i++)
		{
			vis[i]=-1;
		}
		dfs(1,0);
		int cnt1=0,cnt0=0;
		for(int i=1;i<=n;i++)
		{
			if(vis[i])
				cnt1++;
			else
				cnt0++;
		}
		if(cnt0>cnt1)
		{
			printf("%d\n",cnt1);
			for(int i=1;i<=n;i++)
			{
				if(vis[i]==1)
					printf("%d ",i);
			}
		}
		else
		{
			printf("%d\n",cnt0);
			for(int i=1;i<=n;i++)
			{
				if(vis[i]==0)
					printf("%d ",i);
			}
		}
		puts("");
	}
	return 0;
}

F. Destroy it!
题意: 有n轮打击,每一轮打击使用总体力不超过3,且每整十次,技能伤害翻倍,问打出最大伤害是多少
思路: DP题,其实每一轮我们需要最多需要考虑5种技能,3种最大伤害且体力为1,1种伤害且最大体力为2,1种伤害最大且体力为3,因为我们只会优先从这五张选取,其他牌扔掉,所以我们可以先预处理出来
接着对每轮的选取方式进行DP,

选三张 三张1 ;
选两张 两张1 ; 一张2一张1
选一张 一张1 ; 一张2 ; 一张3
注意考虑若在第十应该是最大的翻倍  
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
struct node
{
	int c1,c2,c3;
	ll x1[4],x2,x3;
}a[N];
bool cmp(ll x,ll y)
{
	return x>y;
}
ll dp[10],temp[10];
int main()
{
	int T;
	scanf("%d",&T);
	int n;
	int t=1;
	while(T--)
	{
		scanf("%d",&n);
		vector<ll>tmp;
		tmp.clear();
		for(int i=1;i<=n;i++)
		{
			ll c,d;
			scanf("%lld%lld",&c,&d);
			if(c==1)
			{
				tmp.push_back(d);
				a[t].c1++;
			}
			else if(c==2)
			{
				a[t].c2++;
				a[t].x2=max(a[t].x2,d);
			}
			else
			{
				a[t].c3++;
				a[t].x3=max(a[t].x3,d);
			}
		}
		sort(tmp.begin(),tmp.end(),cmp);
		for(int i=0;i<3&&i<a[t].c1;i++)
		{
			//cout<<tmp[i]<<endl;
			a[t].x1[i+1]=tmp[i];
		}
		t++;
	}
	memset(dp,-1,sizeof dp);
	dp[0]=0;
	//cout<<t<<endl;
	for(int i=1;i<t;i++)
	{
		ll *x1=a[i].x1,x2=a[i].x2,x3=a[i].x3;
		//cout<<x1[0]<<endl;
		int c1=a[i].c1,c2=a[i].c2,c3=a[i].c3;
		memcpy(temp,dp,sizeof dp);
		for(int j=0;j<10;j++)
		{
			if(temp[j]==-1)
				continue;
			//选1张
			if(j<9)
			{
				if(c1)
					dp[j+1]=max(dp[j+1],temp[j]+x1[1]);
				if(c2)
					dp[j+1]=max(dp[j+1],temp[j]+x2);
				if(c3)
					dp[j+1]=max(dp[j+1],temp[j]+x3);
			}
			else
			{
				if(c1)
					dp[0]=max(dp[0],temp[j]+2*x1[1]);
				if(c2)
					dp[0]=max(dp[0],temp[j]+2*x2);
				if(c3)
					dp[0]=max(dp[0],temp[j]+2*x3);
			}
			//选2张
			if(j<8)
			{
				if(c1>=2)
					dp[j+2]=max(dp[j+2],temp[j]+x1[1]+x1[2]);
				if(c2&&c1)
					dp[j+2]=max(dp[j+2],temp[j]+x2+x1[1]);
			}
			else
			{
				if(c1>=2)
					dp[j-8]=max(dp[j-8],temp[j]+2*x1[1]+x1[2]);
				if(c2&&c1)
				{
					ll maxx=max(x2,x1[1]);
					dp[j-8]=max(dp[j-8],temp[j]+x2+x1[1]+maxx);
				}

			}
			//选3张
			if(j<7)
			{
				if(c1>=3)
				{
					dp[j+3]=max(dp[j+3],temp[j]+x1[1]+x1[2]+x1[3]);
				}
			}
			else
			{
				if(c1>=3)
				{
					dp[j-7]=max(dp[j-7],temp[j]+x1[1]*2+x1[2]+x1[3]);
				}
			}
		}
	}
	ll ans=0;
	for(int i=0;i<=9;i++)
	{
		ans=max(ans,dp[i]);
	}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值