P3698 [CQOI2017]小Q的棋盘
题目描述
小 Q 正在设计一种棋类游戏。
在小 Q 设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有 V 个格点,编号为0,1,2 … , V− 1,它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小 Q 在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。
小 Q 现在想知道,当棋子从格点 0 出发,移动 N 步最多能经过多少格点。格点可以重复经过多次,但不重复计数。
输入输出格式
输入格式:第一行包含2个正整数V, N,其中 V 表示格点总数,N 表示移动步数。
接下来V − 1行,每行两个数 ai,bia_i,b_iai,bi ,表示编号为 ai,bia_i,b_iai,bi 的两个格点之间有连线。
输出格式:输出一行一个整数,表示最多经过的格点数量。
输入输出样例
5 2
1 0
2 1
3 2
4 3
3
9 5
0 1
0 2
2 6
4 2
8 1
1 3
3 7
3 5
5
说明
【输入输出样例 1 说明】
从格点 0 出发移动 2 步。经过 0, 1, 2 这 3 个格点。
【输入输出样例 2 说明】
一种可行的移动路径为 0 → 1 → 3 → 5 → 3 → 7,经过 0, 1, 3, 5, 7 这 5 个格点。
【数据规模与约定】
对于 100%的测试点,N,V ≤ 100, 0 ≤a_i,b_i< V
Solution:
本题贪心神了,$100$的数据范围是送给那些写dp的人的,要我就把数据搞成$10^6$。
首先,题目等价于给定了一棵以$0$为根的树,既然是一棵树那么点与点间都是简单路径,若我们知道点与点间的距离,显然从任一点到另一点再回到起点所需要的距离是这两点距离的两倍,要使访问的点数越多,贪心的想到任意一条边我们最少走一次,至多走两次,走一次的边所连的路径越长访问点越多,所以我们直接从根节点走到深度最深的点路径最长,若步数已经大于给定步数,那么答案就是给定步数+1,否则还有多余的步数,将多余的步数除以2就是走两次的边能访问的点数,可以理解为先走这些走两次的边并回到了最深路径上的某点后(最深路径指根节点到最深节点的简单路径,某点不一定是根节点)再走向深度最深的点,累加若超过给定点数,答案就是给定点数,否则就是累加的值。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) using namespace std; const int N=100005; int v,n,to[N],net[N],h[N],cnt,ans; bool vis[N]; il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;} il void dfs(int u,int tot){ ans=max(ans,tot),vis[u]=1; for(int i=h[u];i;i=net[i]) if(!vis[to[i]]) dfs(to[i],tot+1); } int main(){ ios::sync_with_stdio(0); cin>>v>>n; int x,y; For(i,1,v-1) cin>>x>>y,add(x,y),add(y,x); dfs(0,1); if(ans>n)cout<<n+1,exit(0); ans+=(n-ans+1)/2; cout<<(ans>v?v:ans); return 0; }