CF遇难帖

1734D Slime Escape

题意:一开始你在\(k\)的位置并且有初始生命值,要走到\(1\)或者\(n\),每走一步可能掉血也可能加血,问是否能走出去

思路:以向右走为例,算出从\(k+1\)一直到哪个位置能使自己血量之和增加,记这个位置为\(r\),并且计算出从\(k+1\)~\(r\)至少要有多少生命值才能走过来,重复上述操作
将所有地块分割完后,如果自身血量大于相邻的块所需要的生命值的话,肯定就走过去,因为自身血量总会得到增加,另外,如果合并了一些块之后能直接莽到终点,便直接输出即可

A Wide, Wide Graph

显然的结论是当一个\(k\)大到对于某个点来说没有与之距离大于等于\(k\)的点的话,这个点会被孤立,否则会与其它未孤立的点形成一个连通块。

个人思路:一开始以为是换根\(dp\),处理出以每个节点为根的最大深度,后面觉得麻烦就没做了,感觉是可行的。

题解:随意从一个点\(dfs\),找到深度最深的点\(l\)(如果有多个则随意选一个),在以\(l\)为起点\(dfs\)找到最深的点\(r\)(则\(l->r\)为树的直径),处理出每个点到\(l,r\)较长的那个距离,统计答案即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define inf 1e18
#define inc 0xcfcfcfcf
#define N 100007
#define M 500007
#define mod 1000000007
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
using namespace std;
struct Edge
{
	int nxt,to;
}edge[N<<1];
int T=1,n,m,ecnt;
int dep[N],head[N];
inline int Read()
{
	char ch=getchar();bool f=0;int x=0;
	for(;!isdigit(ch);ch=getchar())if(ch=='-')f=1;
	for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	if(f==1)x=-x;return x;
}
void Add(int u,int v)
{
	edge[++ecnt]=(Edge){head[u],v};
	head[u]=ecnt;
}
void Dfs(int u,int fa)
{
	dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa)
			continue;
		Dfs(v,u);
	} 
}
int d1[N],ans[N];
bool Solve()
{
 	//freopen("test.in","r",stdin);
	n=Read();
	for(int i=1;i<n;++i)
	{
		int u=Read(),v=Read();
		Add(u,v);
		Add(v,u);
	}
	dep[0]=-1;
	Dfs(1,0);
	int l=0,r=0,mx=0;
	for(int i=1;i<=n;++i)
		if(dep[i]>mx)
			mx=dep[i],l=i;	
	dep[0]=-1;
	Dfs(l,0);mx=0;
	for(int i=1;i<=n;++i)
	{
		d1[i]=dep[i];
		if(dep[i]>mx)
			mx=dep[i],r=i;	
	}
	dep[0]=-1;
	Dfs(r,0);
	for(int i=1;i<=n;++i)
		d1[i]=max(d1[i],dep[i]),ans[d1[i]+1]++;
	int now=1;
	for(int i=1;i<=n;++i)
	{
		now+=ans[i];
		printf("%lld ",min(now,n));
	}
	printf("\n");
	return true;
}
signed main()
{
	//T=Read();
	while(T--)
		if(!Solve())
			printf("-1\n");
	return 0;
}
/*
5
2 2
1
4
1 2 1
2 5 1

*/


posted @ 2022-11-16 19:28  模拟退火  阅读(39)  评论(0)    收藏  举报