您的位置:首页 > 其它

BJ 集训测试11 捕鱼

2018-03-27 00:09 302 查看
http://www.elijahqi.win/archives/2792

相当于两个人在博弈 先手可以删除一条边或者添加一条边 后手可以走一条边 然后再删除一条边 给定起点终点 后手使得先手操作数最多 先手使得操作数最少 求先手的操作数最少是?

考虑将终点作为树根,如果不在叶子节点那么显然我会把代价最大的那条边封死然后后手就会走代价次大的那个边 同时我还需要用1的代价重新使得他回来那么凑巧 代价就是这个点的叶子数假如我只有一个 那么答案显然是1 因为我直接封死他即可

考虑起点和终点相邻的情况下后手一定往远离起点的方向走那么我们可以在o(n)的时间内dfs解决

如果没有直接相连怎么办我可以二分一下我答案的最大值 然后每次计算的时候 假设这个点在b~a的路径上且该点为u 即u是路径上某一点子树里的点 如果我在到达他之前需要封的点加上该点需要操作数大于我的二分答案了就说明这个一定得封死要不然答案肯定比Mid大

#include<cstdio>
#include<algorithm>
#define N 1000010
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0;char ch=gc();
while(ch<'0'||ch>'9') ch=gc();
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
return x;
}
struct node{
int y,next;
}data[N<<1];
int fa
,h
,leaf
,w
,a,b,n,num;
inline void dfs(int x){
int cnt=0,mx1=0,mx2=0;
for (int i=h[x];i;i=data[i].next){
int y=data[i].y;if (y==fa[x]) continue;fa[y]=x;++cnt;dfs(y);
if (w[y]>mx1) mx2=mx1,mx1=w[y];else if (w[y]>mx2) mx2=w[y];
}w[x]=(leaf[x]=cnt)+mx2;
}
inline bool check(int md){
int last=a,x=a,all=1,ans=0,sum=0;
for (int i=a;i!=b;i=fa[i]) all+=leaf[i]-1;
while(x!=b){int ans1=0;++sum;
for (int i=h[x];i;i=data[i].next){
int y=data[i].y;if (y==last||y==fa[x]) continue;
if (all+w[y]+ans>md) ++ans1,--sum;if(sum<0) return 0;
}ans+=ans1;all-=leaf[x]-(last!=x);last=x;x=fa[x];if (ans>md) return 0;
}return ans<=md?1:0;
}
int main(){
freopen("t3.in","r",stdin);
n=read();b=read();a=read();
for (int i=1;i<n;++i){
int x=read(),y=read();
data[++num].y=y;data[num].next=h[x];h[x]=num;
data[++num].y=x;data[num].next=h[y];h[y]=num;
}
dfs(b);int l=0,r=n<<1;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) r=mid-1;else l=mid+1;
}printf("%d\n",l);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: