目录
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]x−y=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=sumnowx−y=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;
}