题目描述
传送门
题目大意:给出一个有根树,A,B两个人轮流操作。
给每个叶子节点分配权值[1..叶子节点数]的一个排列。每次每个人可以选择任意一条边走,A希望最后取得的叶子节点的权值尽可能大,B希望最后取得的叶子节点的权值尽可能的小,两个人都绝对聪明,问如果让A分配权值最后能得到的最大权值是多少,如果让B分配权值最后能得到的最小权值是多少。
题解
已A的操作为例。
f[i]表示到达节点i能获得其子树中第几大的权值。显然A可以控制奇数层的节点,B可以控制偶数层的节点。
轮到A操作的时候,他一定会选择
min(f[son])
,安排权值的时候把所有最大的都放置到一棵子树上。
轮到B操作的时候,他会尽可能选择小的,所以A的目标就是使最小值最大,那么最好的方式就是平均分配。
最后B只能得到
∑f[son]
B的操作也是一样的,不过用f[i]表示第几小即可。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 400003
using namespace std;
int n,tot,nxt[N],v[N],point[N],f[N],deep[N],leaf;
void add(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs_A(int x,int fa)
{
deep[x]=deep[fa]+1;
bool pd=false;
if (deep[x]&1) {
f[x]=n;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==fa) continue;
dfs_A(v[i],x);
f[x]=min(f[x],f[v[i]]);
pd=true;
}
}
else {
for (int i=point[x];i;i=nxt[i]){
if (v[i]==fa) continue;
dfs_A(v[i],x);
f[x]=f[x]+f[v[i]];
pd=true;
}
}
if (!pd) f[x]=1,leaf++;
}
void dfs_B(int x,int fa)
{
bool pd=false;
if (!(deep[x]&1)){
f[x]=n;
for (int i=point[x];i;i=nxt[i]) {
if (v[i]==fa) continue;
dfs_B(v[i],x);
f[x]=min(f[x],f[v[i]]);
pd=true;
}
}
else {
f[x]=0;
for (int i=point[x];i;i=nxt[i]){
if (v[i]==fa) continue;
dfs_B(v[i],x);
f[x]=f[x]+f[v[i]];
pd=true;
}
}
if (!pd) f[x]=1;
}
int main()
{
freopen("a.in","r",stdin);
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);
printf("%d ",leaf-f[1]+1);
dfs_B(1,0);
printf("%d\n",f[1]);
}