CF Round #635 div2 题解

\(Codeforces\) \(Round\) \(635\)

A. Ichihime and Triangle

\(Description:\)

给定四个整数\(a,b,c,d\),找到三个整数\(x,y,z\)满足\(a\leq x\leq b\leq y\leq c\leq z\leq d\)
使得\(x,y,z\)能构成三角形

\(Solution:\)

签到题,直接令\(x=b,y=c,z=\)c即可,小边尽可能大,大边尽可能小

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		printf("%d %d %d\n",b,c,c);
	}
	return 0;
}

B. Kana and Dragon Quest game

\(Description:\)

一条龙有\(h\)的生命值,你总共有两种技能可以释放:
1.将龙的血量变为\(\lfloor \frac{h}{2} \rfloor+10\)
2.将龙的血量变为\(h-10\)
已知第一个技能可以用\(n\)次,第二个技能可以用\(m\)次,请问能否打败巨龙

\(Solution:\)

我的做法是\(dp\)
\(f[i][j]\)表示用\(1\)技能\(i\)次,用\(2\)技能\(j\)次后龙的最小血量
跑完\(dp\)之后扫一遍看有没有\(0\)出现即可

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
int t,h,n,m,f[35][35];
int main(){
	int i,j;
	scanf("%d",&t);
	while(t--){
		memset(f,0x3f,sizeof(f));
		scanf("%d%d%d",&h,&n,&m);
		f[0][0]=h;
		for(i=0;i<=n;i++){
			for(j=0;j<=m;j++){
				if(i<n)f[i+1][j]=max(0,min(f[i+1][j],f[i][j]/2+10));
				if(j<m)f[i][j+1]=max(0,min(f[i][j+1],f[i][j]-10));
			}
		}
		bool flag=0;
		for(i=0;i<=n;i++)
			for(j=0;j<=m;j++)
				if(f[i][j]==0)flag=1;
		if(flag)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

C. Linova and Kingdom

\(Description:\)

给你一棵以\(1\)为根节点的树,你可以在这棵树上设定\(k\)个关键节点
使得所有非关键节点到根的路径上关键节点数的总和最大
求出这个最大值

\(Solution:\)

手算几组数据之后发现在最优的情况下,如果一个节点是关键节点,那么它的父节点也是关键节点
那么此时增加一个关键节点所获得的收益就是\(size_i-depth_i\)
其中\(size\)为以\(i\)为根节点的子树的大小,\(depth\)\(i\)的深度
我们直接一个\(dfs\)处理出\(size\)\(depth\),然后排序统计答案即可

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
typedef long long lol;
int n,m,head[200005],size;
struct node{
	int to,next;
}edge[500005];
struct point{
	int x,son,depth;
}p[200005];
void putin(int from,int to){
	size++;
	edge[size].to=to;
	edge[size].next=head[from];
	head[from]=size;
}
void dfs(int r,int fa,int dep){
	int i;
	p[r].son=1;p[r].depth=dep;
	for(i=head[r];i!=-1;i=edge[i].next){
		int j=edge[i].to;
		if(j!=fa){
			dfs(j,r,dep+1);
			p[r].son+=p[j].son;
		}
	}
}
bool cmp(const point a,const point b){
	return a.son-a.depth>b.son-b.depth;
}
int main(){
	int i,j;
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);m=n-m;
	for(i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		putin(u,v);
		putin(v,u);
	}
	for(i=1;i<=n;i++)p[i].x=i;
	dfs(1,0,1);
	sort(p+1,p+n+1,cmp);
	lol ans=0;
	for(i=1;i<=m;i++)
		ans+=p[i].son-p[i].depth;
	printf("%lld\n",ans);
	return 0;
}

D. Xenia and Colorful Gems

\(Description:\)

给定三个数列,在每个数列中分别选取一个数\(x,y,z\)
使得\((x-y)^+(y-z)^2+(x-z)^2\)最小,并输出这个最小值

\(Solution:\)

先把三个数列排序,然后让\(i,j,k\)分别指向三个数列的最小值
再贪心的选取\(i,j,k\)中的\(+1\),使得变化后的\((x-y)^+(y-z)^2+(x-z)^2\)最小
不是很懂这个贪心策略为什么是对的,比赛的时候也只是碰碰运气,结果莫名其妙的对了
显然正解因该是枚举第一个数列的数,然后对另外连个数列进行二分查找

\(Code:\)

#include<bits/stdc++.h>
#define inf (9223372036854775807)
using namespace std;
typedef long long lol;
int t,A,B,C;
lol a[100005],b[100005],c[100005];
lol cal(int i,int j,int k){
	return (a[i]-b[j])*(a[i]-b[j])+(b[j]-c[k])*(b[j]-c[k])+(a[i]-c[k])*(a[i]-c[k]);
}
int main(){
	int i,j,k;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d%d",&A,&B,&C);
		for(i=1;i<=A;i++)scanf("%lld",&a[i]);
		for(i=1;i<=B;i++)scanf("%lld",&b[i]);
		for(i=1;i<=C;i++)scanf("%lld",&c[i]);
		sort(a+1,a+A+1);
		sort(b+1,b+B+1);
		sort(c+1,c+C+1);
		i=1;j=1;k=1;
		lol ans=inf;
		while(i<=A&&j<=B&&k<=C){
			lol tmp=cal(i,j,k),sum1=i<A?cal(i+1,j,k):inf,sum2=j<B?cal(i,j+1,k):inf,sum3=k<C?cal(i,j,k+1):inf;
			ans=min(ans,tmp);
			sum1=sum1-tmp;sum2=sum2-tmp;sum3=sum3-tmp;
			if(sum1<=sum2&&sum1<=sum3)i++;
			else if(sum2<=sum1&&sum2<=sum3)j++;
			else if(sum3<=sum1&&sum3<=sum2)k++;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

E. Kaavi and Magic Spell

\(Description:\)

给定两个字符串\(s,t\),现在你有一个空串\(a\),每次操作可以将\(s\)的首字母提取出来并放在\(a\)的最前面或者最后面
操作过程中使得\(a\)的前缀是\(t\)的情况有多少种

\(Solution:\)

显然\(lent\leq lens\),我们不妨将\(t\)拓展到和\(s\)一样长,其中拓展的一部分可以是任意字母
定义状态\(f[l][r]\)表示当前状态下,操作过程中a的前缀为\(t_l\sim t_r\)的情况数
外层循环遍历\(s\),枚举当前要添加的字母,那么
如果当前枚举到的\(s_i=t_r\),那么\(f[l][r]=f[l][r]+f[l][r-1]\)
特别注意的是,由于\(t\)的延长部分可以为任何字母,所以一旦\(r>lent\),也要执行上述转移
如果当前枚举到的\(s_i=t_l\),那么\(f[l][r]=f[l][r]+f[l+1][r]\)
特别注意的是,由于\(t\)的延长部分可以为任何字母,所以一旦\(l>lent\),也要执行上述转移
最后统计以下\(f[1][lent\sim lens]\)的和即可

\(Code:\)

#include<bits/stdc++.h>
#define mod (998244353)
using namespace std;
char s[3005],t[3005];
int lens,lent,f[3005][3005];
int main(){
	int i,j,len;
	scanf("%s%s",s+1,t+1);
	lens=strlen(s+1);lent=strlen(t+1);
	for(i=1;i<=lens+1;i++)f[i][i-1]=1;
	for(i=1,len=1;i<=lens;i++,len++){
		for(int l=1,r=l+len-1;r<=lens;l++,r++){
			if(l>lent||s[i]==t[l])f[l][r]=(f[l][r]+f[l+1][r])%mod;
			if(r>lent||s[i]==t[r])f[l][r]=(f[l][r]+f[l][r-1])%mod;
		}
	}
	int ans=0;
	for(i=lent;i<=lens;i++)
		ans=(ans+f[1][i])%mod;
	printf("%d\n",ans);
	return 0;
}

BBT

运气比较好吧,有几个结论是猜对的。
但是还是被自己菜到:
\(T2\)明明是个数学题,偏偏要去整什么dp,简直就是耽误比赛时间。。。
\(T3\)交的时候手抖选错编译器,编译错误一次,简直是在搞笑,改完之后还没有一遍过,菜的不行。。。
\(T4\)那么简单的二分查找想不到,非要去整一个自己都不确定的贪心。。。不过还好,蒙对了(手动狗头)

posted @ 2020-04-16 20:34  kakakakakaka  阅读(185)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效