Codeforces Round #660 (Div. 2) A B C

A. Captain Flint and Crew Recruitment

题意

  • 链接Captain Flint and Crew Recruitment
  • 定义一种数x叫nearly prime,x可表示为x=p*q,其中 1<p<q 且p、q均为素数
    给出n问n能否表示成4个不同的整数之和且这4个数中至少有3个是nearly prime

解题思路

  • 最小的3个nearly prime分别是6=23 10=25 14=2*7
    所以n至少要大于6+10+14=30才能符合条件,且4个数分别为6 10 14 n-30
  • 需要注意的是4个不同的整数之和,所以以下三种情况需要特殊处理
    n-30=6 \quad n=36=6+10+15+5
    n-30=10 \quad n=40=1+10+15+14
    n-30=14 \quad n=44=5+10+15+14

代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
	int t,n,ans;
	int x=6+10+14;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		if(n<=x)printf("NO\n");
		else 
		{
			printf("YES\n");
			if(n==36)printf("6 10 15 5\n");
			else if(n==40)printf("1 10 15 14\n");
			else if(n==44)printf("5 10 15 14\n");
			else printf("6 10 14 %d\n",n-x);
		}
	}
	return 0;
} 

B. Captain Flint and a Long Voyage

题意

  • 链接Captain Flint and a Long Voyage
  • 对于一个n长度的正整数x,我们定义一个k,是他们的每一位的数的二进制数组合而成,比如x=729,k=111101001,将k的后n位去掉形成新的数r=111101,现在给定n,要求算出最小的x,使得它所形成的的r最大。

解题思路

  • 因为去掉k的后n位,所以要保证前面的要越大越好即每位都是9。
  • 8=1000,9=1001,7=111,…… 应该选择二进制有4位且0最多的8来做后面的数
  • 所以后 ┌ n / 4 ┐ ┌n/4 ┐ n/4(取上整)位为8,剩下的位为9即为答案

代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
	int t,n,ans;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		int cnt=1;
		if(n>4&&n%4==0)cnt=n/4;
		else if(n>4&&n%4!=0)cnt=1+n/4;
		int x=n-cnt;
		for(int i=1;i<=x;i++)printf("9");
		for(int i=1;i<=cnt;i++)printf("8");
		printf("\n");
	}
	return 0;
} 

C. Uncle Bogdan and Country Happiness

参考文章:传送门

题意

  • 链接Uncle Bogdan and Country Happiness
  • 给定一个n个结点的树形图,每个节点代表一个城市,现在有m个人要从节点1回家,家在结点i的人数有p[i],在回家的过程中会每个人可能从心情好变成心情坏,但是不能从心情坏变成心情好,现在每个城市i统计路过的人中心情好的人数-心情坏的人数的值h[i],判断每个城市统计的h[i]是否正确。

解题思路

  • 首先分析一下问题
    每个人都是从节点1走回自己家,而且一定是前一段路的心情好(可能前一段路长为0),后一段路心情差
    我们的难点在哪里?
    在于有很多个人,我们不能确定每个人在哪个节点心情开始变差的,但其实我们是可以大概确定的

  • Ⅰ.考虑叶子节点
    对于一个叶子节点w来说,能到达w的都是家在w的,设这些人到达时有x个人心情好,y个人心情坏,则
    { x + y = p [ w ] x − y = h [ w ] \begin{cases} x+y=p[w]\\ x−y=h[w] \end{cases} {x+y=p[w]xy=h[w]
    所以x=(p[w]+h[w])/2, y=p[w]−x
    其中(pw+hw)应该是偶数

  • Ⅱ.考虑非叶子节点k
    这一步其实就是由叶子节点往上推的
    结点k的最少心情好人数是所有子节点心情好人数相加,记作dp[k][1]
    结点k的最大心情坏人数是所有子节点心情坏人数和+p[k],记作dp[k][2](因为这p[k]个人在k节点可能开心,也可能不开心,若都不开心那么人数就是最大,所以要+p[k])
    那么当前经过这个点k的总人数是sumnow=dp[k][1]+dp[k][2],我们还是设到k的人中有x个人心情好,y个人心情坏,则
    { x + y = s u m n o w x − y = h [ w ] \begin{cases} x+y=sumnow\\ x−y=h[w] \end{cases} {x+y=sumnowxy=h[w]
    所以x=(sumnow+h[w])/2, y=sumnow−x
    则(sumnow+hw)应该是偶数
    因为子节点心情好的人在上一个节点一定心情好(只能由心情好->心情坏)但是可能还存在一些人在k节点心情好,但是跑到子节点就心情坏了,所以这里应该满足x>=dp[k][1]
    判断完之后,由于k点的心情好,心情坏人数被计算出来了,那么dp[k][1]=x,dp[k][2]=y,然后继续向上推

  • 对任意一个节点w满足经过w的总人数>=abs(h[w])

代码

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=1e5+5;
int t,n,m,flag,dp[maxn][5],p[maxn],h[maxn];
vector<int>e[maxn];
void dfs(int now,int fa)
{
	if(e[now].size()==1&&now!=1)//到达叶子节点 
	{
		if(p[now]<abs(h[now]))flag=0;
		if((p[now]+h[now])%2==1)flag=0;
		dp[now][1]=(p[now]+h[now])/2;
		dp[now][2]=p[now]-dp[now][1];
		return;
	}
	int len=e[now].size();
	for(int i=0;i<len;i++)
	{
		int v=e[now][i];
		if(v==fa)continue;
		dfs(v,now);
		dp[now][1]+=dp[v][1];
		dp[now][2]+=dp[v][2];
	}
	dp[now][2]+=p[now];
	int sumnow=dp[now][1]+dp[now][2];
	if(sumnow<abs(h[now]))flag=0;
	if((sumnow+h[now])%2==1)flag=0;
	int x=(sumnow+h[now])/2;
	if(x<dp[now][1])flag=0;
	int y=sumnow-x;
	dp[now][1]=x;dp[now][2]=y;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)scanf("%d",&p[i]);
		for(int i=1;i<=n;i++)scanf("%d",&h[i]);
		int x,y;
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			e[x].push_back(y);
			e[y].push_back(x);
		}
		flag=1;
		dfs(1,0);
		if(flag)printf("YES\n");
		else printf("NO\n");
		for(int i=1;i<=n;i++)//清空 
		{
			dp[i][1]=dp[i][2]=0;
			e[i].clear();
		}
	}
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值