Codeforces Round #527 (Div. 3) CDEF题解

C. Prefixes and Suffixes

题意:有一个长度为 n 的的字符串,现在给出长度1到n-1的该串前缀和后缀(混合的),求哪些串是前缀,哪些是后缀。

思路:为啥我感觉这套比赛除了E题,就C题最难.....先找到两个最长的串,假设其中一个为前缀另外一个为后缀即可。

#include<bits/stdc++.h>
using namespace std;
char pre[105],sa[105];
int n,vis[201];
struct node
{
	char s[105];
	int id;
	bool operator<(const node& t)const
	{
		return strlen(s)<strlen(t.s);
	}
}a[205];
int isp(char s[105])
{
	int m=strlen(s);
	for(int i=0;i<m;i++)
	if(s[i]!=pre[i])return 0;
	return 1;
}
int issa(char s[105])
{
	int m=strlen(s);
	for(int i=0,j=n-1-m;i<m;i++,j++)
	if(s[i]!=sa[j])return 0;
	return 1;
}
void print()
{
	for(int i=1;i<=n*2-2;i++)
	if(vis[i]==1)printf("P");
	else printf("S");
}
int main()
{
	cin>>n;
	for(int i=1;i<=n*2-2;i++)cin>>a[i].s,a[i].id=i;
	sort(a+1,a+n*2-1);
	strcpy(pre,a[n*2-3].s);
	strcpy(sa,a[n*2-2].s);
	int flag=1;
	for(int i=1;i<=n*2-2;i+=2)
	{
		if(isp(a[i].s)&&issa(a[i+1].s))
		vis[a[i].id]=1,vis[a[i+1].id]=2;
		else if(isp(a[i+1].s)&&issa(a[i].s))
		vis[a[i].id]=2,vis[a[i+1].id]=1;
		else flag=0;
	}
	if(flag)print();
	else
	{
		strcpy(sa,a[n*2-3].s);
		strcpy(pre,a[n*2-2].s);
		for(int i=1;i<=n*2-2;i+=2)
		{
			if(isp(a[i].s)&&issa(a[i+1].s))
			vis[a[i].id]=1,vis[a[i+1].id]=2;
			else
			vis[a[i].id]=2,vis[a[i+1].id]=1;
		}		
		print();		
	}
}

D1. Great Vova Wall (Version 1)

题意:给你 n 堆砖头,你可以选择把一堆的高度+2,也可以选择把两堆高度相同的砖头高度同时+1,问是否把 n 堆砖头变得一样高。

思路:只要相邻两堆砖头高度相差的值为2的倍数,就直接成对消掉,最后如果还剩下的砖头堆超过1,那就是NO了。

#include<bits/stdc++.h>
using namespace std;
stack<int>s;
int main()
{
	int n,a;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		if(s.empty())s.push(a);
		else
		{
			if(abs(s.top()-a)%2)s.push(a);
			else s.pop();
		}
	}
	if(s.size()>1)puts("NO");
	else puts("YES");
}

D2. Great Vova Wall (Version 2)

题意:同上,不过这题不能把一堆砖头高度+2,只能把相邻同高的砖头高度+1,问是否能使得所有砖头等高。

思路:发现这两种肯定不行:5 6 6,6 6 5,那我只要看是否存在一堆砖头,它的高度比前面的高(消消乐之后前面还剩下的),以及消消乐结束后只剩下一堆砖头且高度不是最高的,这两种特殊情况肯定输出NO。

#include<bits/stdc++.h>
using namespace std;
stack<int>s;
int main()
{
	int n,a,mx=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a);
		mx=max(mx,a);
		if(s.empty())s.push(a);
		else
		{
			if(a==s.top())s.pop();
			else if(a>s.top())
			{
				puts("NO");
				return 0;
			}
			else s.push(a);
		}
	}
	if(s.size()>1)puts("NO");
	else if(!s.empty()&&s.top()!=mx)puts("NO");
	else puts("YES");	
}

E. Minimal Diameter Forest

题意:给你n个点m条边的森林,你可以添加n-1-m条边使得森林变成一棵树,求最小的树的直径,并输出加边的方案。

思路:先求出森林每棵树的直径,然后dp一下找到每颗树的直径上的中间点,连接这些中间点即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
vector<int>G[maxn];
struct node
{
	int u,len;
	bool operator<(const node& t)const
	{
		return len>t.len;
	}
}a[maxn];
int vis[maxn],d[maxn],rt,mx,cnt,ans;
void dfs(int u,int fa,int dep)
{
	d[u]=dep;
	vis[u]=1;
	if(d[u]>d[rt])rt=u;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs(v,u,dep+1);
	}
}
void dfs2(int u,int fa,int dep)
{
	d[u]=dep;
	mx=max(mx,dep);
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(v==fa)continue;
		dfs2(v,u,dep+1);
		d[u]=max(d[u],d[v]);
	}
	if(d[u]==mx&&dep==mx/2)
	rt=u;
}
int main()
{
	int n,m,u,v;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
	if(!vis[i])
	{
		rt=i,mx=0;
		dfs(i,0,0);
		dfs2(rt,0,0);
		a[++cnt].u=rt,a[cnt].len=mx/2+mx%2;
		ans=max(ans,mx);
	}
	sort(a+1,a+1+cnt);
	if(cnt>1)
	ans=max(ans,a[1].len+a[2].len+1);
	if(cnt>2)
	ans=max(ans,a[2].len+a[3].len+2);
	printf("%d\n",ans);
	for(int i=2;i<=cnt;i++)
	printf("%d %d\n",a[1].u,a[i].u);
}

F. Tree with Maximum Cost

题意:给你一棵树,每个点有权值ai,如果你任意选择一个点 u 作为根,你会得到一个cost=ai*dist( u , i )(i 取1到n),求出最大的cost。

思路:简单树形dp,先求出 1 为根的答案,设d[ i ]为以 i 为根的子树的总权值,如果我知道了 u 为根的答案,我想求 u 的儿子 v 为根的答案,我发现可以递推过来,设之前的答案为f[ u ],f[ v ]=f[ u ]+d[ 1 ] - 2*d[ v ] ,因为我现在的根到达 v 的子树所有点距离都减少了 1 ,答案就减少d[ v ],达到所有非 v 的子树的点的距离都增加了1,答案就增加d[ 1 ] - d[ v ]。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
vector<int>G[maxn];
ll a[maxn],d[maxn],ans;
void dfs(int u,int fa,int dep)
{
	d[u]=a[u];
	ans+=a[u]*dep;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(fa==v)continue;
		dfs(v,u,dep+1);
		d[u]+=d[v];
	}
}
void dfs2(int u,int fa,ll res)
{
	ans=max(res,ans);
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i];
		if(fa==v)continue;
		dfs2(v,u,res+d[1]-d[v]*2);		
	}
}
int main()
{
	int n,u,v;
	cin>>n;
	for(int i=1;i<=n;i++)
	scanf("%lld",&a[i]);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0,0);
	dfs2(1,0,ans);
	cout<<ans;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

长沙橘子猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值