题目描述:
一棵n个结点的有根树,其中有m个叶结点,每个叶子结点上有一个数字
可以任意排列叶子上的数字(1~m)
根结点上有一个棋子,双方轮流操作,只能从父结点移动到子结点
游戏的得分就是叶子结点上的编号
玩家一希望最大化得分,玩家二希望最小化得分
求最大得分和最小得分
分析:
努力yy出了一种naive的方法:
显然,奇数层会被先手(最大化得分)控制,偶数层会被后手(最小化得分)控制
每个人一定会选择叶结点个数较少的子树进入,这样留给另一个人的选择就会尽量少
对于先手,我们可以把尽量大的值放到这个子树中
对于后手,我们就把尽量小的值放到这个子树中
后来想了想好像不是很完善,代码也不是很好完成
f[i] f [ i ] 表示到达结点 i i 能获得其子树中第几大的权值
先手一定会选择,安排权值的时候把最大的权值都放置到一棵子树上
轮到后手操作的时候,ta会尽可能选择排名较后的
而先手的目标就是使最小值最大,那么最好的方式就是平均分配
最后后手只能得到
∑f[son]
∑
f
[
s
o
n
]
对于后手, f f 的维护也是一样的,用表示第几小即可
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=200005;
struct node{
int y,nxt;
};
node way[N<<1];
int st[N],tot=0,leaves=0,f[N],n;
void add(int u,int w) {
tot++;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].nxt=st[w];st[w]=tot;
}
void dfs_A(int now,int fa,int flag) { //f 第几大
int ch=0;
if (flag) { //先手局
f[now]=n;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa) {
dfs_A(way[i].y,now,flag^1);
f[now]=min(f[now],f[way[i].y]);
ch++;
}
}
else {
f[now]=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa) {
dfs_A(way[i].y,now,flag^1);
f[now]+=f[way[i].y];
ch++;
}
}
if (!ch) f[now]=1,leaves++;
}
void dfs_B(int now,int fa,int flag) { //f 第几小
int ch=0;
if (!flag) {
f[now]=n;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa) {
dfs_B(way[i].y,now,flag^1);
f[now]=min(f[now],f[way[i].y]);
ch++;
}
}
else {
f[now]=0;
for (int i=st[now];i;i=way[i].nxt)
if (way[i].y!=fa) {
dfs_B(way[i].y,now,flag^1);
f[now]+=f[way[i].y];
ch++;
}
}
if (!ch) f[now]=1;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
dfs_A(1,0,1);
printf("%d ",leaves-f[1]+1);
dfs_B(1,0,1);
printf("%d\n",f[1]);
return 0;
}