问题 L: 开会
时间限制: 1 Sec 内存限制: 128 MB
提交: 111 解决: 20
[提交] [状态] [讨论版] [命题人:admin]
题目描述
开会,是对所有人时间的浪费,是对集体的谋杀。
山区学校的一些学生之间的关系似乎好得有点过头,以至于传出了一些(在风纪委员们看来)不好的绯闻。具体地,有n个学生,n-1条绯闻,每条绯闻的主角都是俩学生。记者们的恶趣味保证任意两个学生,可以通过若干条绯闻直接或间接地联系在一起。
于是学校打算邀请一些学生参加座谈会。
校长相信,假如邀请了某位学生x来开会,那么就能够震慑到x本人,以及和x在同一条绯闻里的学生们。
矿泉水是宝贵的,校长想知道最少需要请多少人来开会,才有可能震慑到所有同学。
输入
第一行是 n 表示学生数。
之后n-1行,每行俩整数x,y,表示学生x和y之间有绯闻。( x≠y,但不一定x<y )
输出
一行,一个整数表示最少要邀请多少人。
样例输入
5
1 3
5 2
4 3
3 5
样例输出
2
提示
可以选择邀请学生2&3,或者是邀请学生3&5
对于前10%的数据,n<=15
对于前30%的数据,n<=2000
对于接下来30%的数据,n<=10^5,且有俩学生需要通过n-1条绯闻才能扯上关系。
对于前100%的数据,n<=10^5,1<=x,y<=n
居然是板子题,嗯,板子还是很好理解的,可惜当时不知道
无向图,两倍的边,所以边开两倍
一遍dfs,
求出某个点的父亲,并且将dfs的顺序放入栈中(需要反序)
那么依次弹出栈中点
如果他没被覆盖(震慑),那么标记他父亲(这个就是被拉去开会的那个),这样覆盖(震慑)掉他,他父亲,他父亲的父亲
栈空即结束
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
struct Edge{
int to,next;
}E[maxn<<1];//边
int Fa[maxn],head[maxn],st[maxn],now,cnt,n;//Fa-father,head-链式前向星,st-栈,now-dfs序,cnt-链式前向星边数,n-树节点数
bool vis[maxn],Set[maxn];//vis是-是否震慑某人,Set是-某人去开会
int ans;//答案
void dfs(int x,int dad){
Fa[x] = dad;
st[now++] = x;//入栈
for(int i=head[x];~i;i = E[i].next){
int v = E[i].to;
if(v == dad)continue;
dfs(v,x);
}
}
void add(int u,int v){
E[cnt] = {v,head[u]};
head[u] = cnt++;
E[cnt] = {u,head[v]};
head[v] = cnt++;
}
void init(){
memset(head,-1,sizeof(head));
memset(Fa,0,sizeof(Fa));
memset(st,0,sizeof(st));
}
int main(){
init();
scanf("%d",&n);
for(int i=0;i<n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
dfs(1,0);
while(now){//栈不空
int top = st[--now];//取栈顶(这里理解为倒序遍历数组也行)
if(!vis[top]){
if(!Set[Fa[top]]){//选其父亲去开会
Set[Fa[top]] = 1;
ans ++;
}
vis[top] = 1;//它
vis[Fa[top]] = 1;//它父亲
vis[Fa[Fa[top]]] = 1;//它爷爷
}
}
printf("%d\n",ans);
return 0;
}