题目描述
Description
山景城毗邻雷克雅未克,位于美国东南部,高楼林立,彻夜灯明,是美国乃至世界的经济贸易中心。最有名的地标建筑当属南岸花园广场,这里不仅是洛杉矶银河队的主场,并且众多国际歌星,如二人转演员蕾哈娜,伟大的人民艺术家 pdd 都曾在这里举办过演唱会。
花园广场球馆的内部由 n 个独立的展厅组成,不同展厅间由 n − 1 条双向的走廊连接而互相可达。二月三十一号那天,花园广场球馆将会对公众开放展览文艺复兴时期的代表作品《蒙娜丽莎的假笑》,怪盗基德闻声披着夜色降临于此。不过 Kris 透过馆内高端的红外线监控设施,很快便发现了基德,怪盗基德降临在 s 号展厅,而 Kris 此刻所在的监控室位于展厅 t,Kris 想引诱基德至展厅 t 以瓮中捉鳖。
从基德进入南岸花园广场球馆后的那一刻开始,双方便开始了斗智斗勇,在第 i 秒钟:
• Kris 在这一秒的开始,可以进行决策:1) 不操作;2) 炸毁一条还存在的走廊,操作过后这条走廊将不复存在;3) 清除一条走廊上被怪盗基德留下标记。
• 怪盗基德在这一秒的末尾,有可能会移动:假设基德此刻正处于 p 号展厅,若从 p 号展厅出发通往相邻展厅的每条走廊都被画上了标记,那么基德会原地不动;否则,基德会从 p 出发的未被标记的走廊中选择一个,走到对应的展厅,并在这条走廊上留下标记(若存在从 p 出发的未被标记的走廊,基德必须移动,不能选择原地停留)。
(注意:以上过程中 Kris 先决策,决策完了之后基德再会行动)
在这一秒的博弈结束后,若怪盗进入了 t 号展厅,博弈停止,否则进入下一秒的博弈。
Kris 炸毁走廊与清除标记都算作一次操作。Kris 希望用尽量少的操作次数抓住基德;而基德希望Kris 用尽量多的操作才能抓住他,并在 Kris 眼皮底下逃之夭夭,以羞辱他。
由于怪盗基德过于神仙,而 Kris 具有上帝视角,双方都十分清楚对方的心思,知道双方的位置,并熟知花园广场球馆的地图。此次是怪盗基德第一次造访花园广场球馆,所以所有的走廊在博弈开始前都没有任何标记。双方都会采取最优决策,请求出 Kris 操作的次数。
Input
第一行,三个整数 n,t,s。
接下来 n−1 行,每行两个整数 ui ,vi ,描述一条连接 ui 与 vi 的通道。
Output
一行,表示答案。
Sample Input
10 1 4
1 2
2 3
2 4
3 9
3 5
4 7
4 6
6 8
7 10
Sample Output
4
Data Constraint
subtask1 20pts,n ≤ 10。
subtask2 20pts,保证存在一条 s 到 t 的走廊。
subtask3 20pts,n ≤ 1000。
subtask4 40pts,没有额外限制。 对于100%的数据,1≤t,s,n≤10^6 。
奇妙的题面
20%
不会
博弈类的题目暴力一般都比正解难写
40%(20%+暴力)
因为给出的是一颗树,而s和t之间又有边
所以一定是这样一个图
即以t为根时,s不可能走到s的其它兄♂弟节点
于是设w[i]表示
最优决策下,基德从i的叶子走到i再走到t的操作数(Kris)
所以上面的图的w是这样的
讲一下w[i]的求法
①i是叶子节点
那么可以直接一路推(从t开始),求出所有相关的边
②i不是叶子节点
因为Kris不希望基德的步数多,所以Kris在基德走到i时一定会清除掉连向最大的节点的边
但是由于Kris每次只能清除一条边,所以对于次大的节点就无能为力了。
因此基德每次都会走向次大节点(如果只有一个那么就直接被困住)
一些疑惑
至于为什么Kris只删同层的边,而不是删更下的边?
举个栗子:
还是刚刚那幅图,比如一开始可以这样删:
但是根据w的式子来看,删掉下面的点并不会使答案更优。
就算删掉的是影响该点的值,那这样不如直接把整棵树删掉。
100%
没有了20%的限制(s可以直接到t),所以s到t之间可能会有其它点
而Kris无法把这些路径上的边删掉,因为删掉了基德就走不到t
还是按照40%定义w
因为无法直接求,所以考虑二分答案。
设其为ans
那么沿着s到t的路径,如果有一个点的子节点的w大于ans,那么这个边肯定要删,如果不删就会比答案大
每次考虑一个点,中途再判断一下不成立的情况:
①删掉的边(设其为cut)>当前可删次数(就是基德一路走来的步数)
②cut>ans
还有记得打人工栈
code
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
int a[2000001][2];
int ls[1000001];
int f[1000001];
int d[1000001];
int num[1000001];
int i,j,k,n,S,T,len,m,Find,N,l,r,mid;
bool Bz[1000001];
int D[1000001][2];
bool bz;
int p[1000001][6];
bool b[1000001];
int Len;
void New(int x,int y)
{
len++;
a[len][0]=y;
a[len][1]=ls[x];
ls[x]=len;
num[x]++;
}
void init()
{
memset(b,0,sizeof(b));
Len=1;
p[1][0]=T;
p[1][1]=0;
p[1][2]=0;
p[1][3]=0;
p[1][4]=0;
p[1][5]=ls[T];
while (Len)
{
for (; p[Len][5] && a[p[Len][5]][0]==p[Len][1]; p[Len][5]=a[p[Len][5]][1]);
if (p[Len][5] && !b[a[p[Len][5]][0]])
{
Len++;
p[Len][0]=a[p[Len-1][5]][0];
p[Len][1]=p[Len-1][0];
p[Len][2]=p[Len-1][2]+num[a[p[Len-1][5]][0]]-1-Bz[a[p[Len-1][5]][0]]+(a[p[Len-1][5]][0]==S);
p[Len][3]=0;
p[Len][4]=0;
p[Len][5]=ls[a[p[Len-1][5]][0]];
}
for (; Len && !p[Len][5]; Len--)
{
f[p[Len][0]]=(!p[Len][4])?p[Len][2]:f[p[Len][4]];
b[p[Len][0]]=1;
}
if (!Len) return;
if (b[a[p[Len][5]][0]])
{
if (f[a[p[Len][5]][0]]>f[p[Len][3]])
p[Len][4]=p[Len][3],p[Len][3]=a[p[Len][5]][0];
else
if (f[a[p[Len][5]][0]]>f[p[Len][4]])
p[Len][4]=a[p[Len][5]][0];
p[Len][5]=a[p[Len][5]][1];
}
}
}
void Fd()
{
int i,h,t;
h=0,t=1;
D[1][0]=T;
D[1][1]=0;
b[T]=1;
while (h<t)
{
for (i=ls[D[++h][0]]; i && !b[a[i][0]]; i=a[i][1])
{
D[++t][0]=a[i][0];
D[t][1]=h;
b[a[i][0]]=1;
if (D[t][0]==S) break;
}
if (D[t][0]==S) break;
}
for (; t; d[++m]=D[t][0],Bz[d[m]]=1,t=D[t][1]);
}
bool pd(int t)
{
int i,j,cut,lastcut;
cut=0;
fo(i,1,m-1)
{
lastcut=cut;
for (j=ls[d[i]]; j; j=a[j][1])
if (!Bz[a[j][0]])
{
if (lastcut+f[a[j][0]]>t)
cut++;
}
if (cut>i || cut>t)
return 0;
}
return 1;
}
int main()
{
freopen("mountainview.in","r",stdin);
freopen("mountainview.out","w",stdout);
scanf("%d%d%d",&n,&T,&S);
fo(i,2,n)
{
scanf("%d%d",&j,&k);
New(j,k),New(k,j);
}
Fd();
init();
l=0,r=n*2;
while (l<r)
{
mid=(l+r)/2;
if (!pd(mid))
l=mid+1; else r=mid;
}
printf("%d\n",l);
fclose(stdin);
fclose(stdout);
return 0;
}
后记
终于把坑填完了。。。
人工栈真™难写
还有这道题根JZOJ5388 一模一样
改个文件名交上去就A了
(不过那题数据极水)