题目描述

  Alice和Bob正在一棵树上玩游戏。这棵树有\(n\)个结点,编号由\(1\)到\(n\)。他们一共玩\(q\)盘游戏。

  在第\(i\)局游戏中,Alice从结点\(a_i\)出发,Bob从结点\(b_i\)出发。开始时,除了\(a_i\)和\(b_i\)这两个结点外,所有结点都没有染色。结点\(a_i\)被Alice染色,结点\(b_i\)被Bob染色。

  接下来,两位玩家轮流移动,两位玩家移动步数之和为\(k_i\)步。Alice走第一步,Bob走第二步,Alice走第三步\(\cdots\)在每一步中,玩家可以移动到相邻的结点并把该结点染色。注意一个结点可以被多次染色:在任意时刻,每个被染过色的结点的颜色为最后到达过该结点的玩家染的颜色。

  记游戏结束时Alice染色的结点数为\(A\) , Bob染色的结点数为\(B\) 。Alice 想要\((A - B)\)尽量大,Bob 想要\((A - B)\)尽量小。如果两个玩家都以最优策略玩的话,我们想知道最后的\((A - B)\)值是多少。

  \(n,q\leq 20000\)

题解

  设两人之间距离为\(d\)。

  两个人可以相遇或不相遇。

  相遇:

  \(k\)奇\(d\)偶:\(1\)

  \(k\)奇\(d\)奇:\(2\)

  \(k\)偶\(d\)偶:\(-1\)

  \(k\)偶\(d\)奇:\(0\)

  不相遇:

  \(k\)奇:1

  \(k\)偶:0

  如果\(k\)奇,那么对\(Bob\)来说不相遇更优,他就需要逃跑。

  如果\(k\)偶,那么\(Alice\)就要逃跑。

  判断逃跑是否成功就是看这个人在不经过另一个人能到达的点的情况下能走多少步。

  可以发现逃跑路径的终点一定是直径的一个端点。

  可以通过树形DP求出子树内直径和子树外直径。

  时间复杂度:\(O(n+q\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> g[200010];
int n,q;
struct node
{
int f,t,w,d,s,ms;
};
node a[200010];
int ti;
int w[200010];
void dfs(int x,int fa,int dep)
{
a[x].f=fa;
a[x].d=dep;
a[x].s=1;
int s=0;
for(vector<int>::iterator p=g[x].begin();p!=g[x].end();p++)
{
int v=*p;
if(v!=fa)
{
dfs(v,x,dep+1);
a[x].s+=a[v].s;
if(a[v].s>s)
{
s=a[v].s;
a[x].ms=v;
}
}
}
}
void dfs2(int x,int top)
{
a[x].t=top;
a[x].w=++ti;
w[ti]=x;
if(a[x].ms)
dfs2(a[x].ms,top);
for(vector<int>::iterator p=g[x].begin();p!=g[x].end();p++)
{
int v=*p;
if(v!=a[x].f&&v!=a[x].ms)
dfs2(v,v);
}
}
int getlca(int x,int y)
{
while(a[x].t!=a[y].t)
if(a[a[x].t].d>a[a[y].t].d)
x=a[a[x].t].f;
else
y=a[a[y].t].f;
return a[x].d<a[y].d?x:y;
}
int jump(int x,int d)
{
while(a[x].w-a[a[x].t].w<d)
{
d-=a[x].w-a[a[x].t].w+1;
x=a[a[x].t].f;
}
return w[a[x].w-d];
}
struct p1{int d,x;p1(int a=0,int b=0):x(a),d(b){}};
int operator <(p1 a,p1 b){return a.d<b.d;}
int operator >(p1 a,p1 b){return a.d>b.d;}
p1 operator +(p1 a,int b){a.d+=b;return a;}
struct p2{int d,x,y;p2(int a=0,int b=0,int c=0):x(a),y(b),d(c){}};
int operator <(p2 a,p2 b){return a.d<b.d;}
int operator >(p2 a,p2 b){return a.d>b.d;}
p2 operator +(p1 a,p1 b){p2 c;c.d=a.d+b.d;c.x=a.x;c.y=b.x;return c;}
p2 operator +(p2 a,int b){a.d+=b;return a;}
p1 f1[200010];
p2 f2[200010];
p1 fir[200010];
p1 sec[200010];
p1 thi[200010];
p2 fir1[200010];
p2 sec1[200010];
p1 g1[200010];
p2 g2[200010];
void dfs3(int x)
{
f1[x]=p1(x,1);
f2[x]=p2(x,x,1);
fir[x].d=sec[x].d=thi[x].d=fir1[x].d=sec1[x].d=-1;
for(vector<int>::iterator p=g[x].begin();p!=g[x].end();p++)
{
int v=*p;
if(v!=a[x].f)
{
dfs3(v);
f2[x]=max(f2[x],f1[x]+f1[v]);
f2[x]=max(f2[x],f2[v]);
f1[x]=max(f1[x],f1[v]+1);
if(f1[v]>fir[x])
{
thi[x]=sec[x];
sec[x]=fir[x];
fir[x]=f1[v];
}
else if(f1[v]>sec[x])
{
thi[x]=sec[x];
sec[x]=f1[v];
}
else if(f1[v]>thi[x])
thi[x]=f1[v];
if(f2[v]>fir1[x])
{
sec1[x]=fir1[x];;
fir1[x]=f2[v];
}
else if(f2[v]>sec1[x])
sec1[x]=f2[v];
}
}
}
void dfs4(int x)
{
for(vector<int>::iterator p=g[x].begin();p!=g[x].end();p++)
{
int v=*p;
if(v!=a[x].f)
{
g1[v]=p1(x,1);
g2[v]=p2(x,x,1);
if(f1[v].x==fir[x].x)
{
g2[v]=max(g2[v],max(max(sec[x]+thi[x],sec[x]+g1[x]),thi[x]+g1[x])+1);
g2[v]=max(g2[v],max(sec[x],g1[x])+p1(x,1));
g1[v]=max(g1[v],max(sec[x],g1[x])+1);
}
else if(f1[v].x==sec[x].x)
{
g2[v]=max(g2[v],max(max(fir[x]+thi[x],fir[x]+g1[x]),thi[x]+g1[x])+1);
g2[v]=max(g2[v],max(fir[x],g1[x])+p1(x,1));
g1[v]=max(g1[v],max(fir[x],g1[x])+1);
}
else
{
g2[v]=max(g2[v],max(max(fir[x]+sec[x],fir[x]+g1[x]),sec[x]+g1[x])+1);
g2[v]=max(g2[v],max(fir[x],g1[x])+p1(x,1));
g1[v]=max(g1[v],max(fir[x],g1[x])+1);
}
if(f2[v].x==fir1[x].x)
g2[v]=max(g2[v],max(sec1[x],g2[x]));
else
g2[v]=max(g2[v],max(fir1[x],g2[x]));
dfs4(v);
}
}
}
int getdist(int x,int y)
{
return a[x].d+a[y].d-2*a[getlca(x,y)].d;
}
int gao(int x,int y,int lca,int d)
{
int dist=a[x].d+a[y].d-2*a[lca].d;
if(dist<=d)
return -1;
if(d>=a[x].d-a[lca].d)
{
int z=jump(y,dist-d-1);
int x1=f2[z].x;
int x2=f2[z].y;
return max(getdist(y,x1),getdist(y,x2));
}
else
{
int z=jump(x,d);
int x1=g2[z].x;
int x2=g2[z].y;
return max(getdist(y,x1),getdist(y,x2));
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
scanf("%d%d",&n,&q);
int i,x,y;
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0,1);
dfs2(1,1);
dfs3(1);
g1[1]=p1(1,-1);
g2[1]=p2(1,1,-1);
dfs4(1);
int k;
for(i=1;i<=q;i++)
{
scanf("%d%d%d",&x,&y,&k);
int lca=getlca(x,y);
int d=a[x].d+a[y].d-2*a[lca].d;
if(k&1)
{
if(gao(x,y,lca,(k+1)/2)>=k/2)
printf("1\n");
else if(d&1)
printf("2\n");
else
printf("1\n");
}
else
{
if(gao(y,x,lca,k/2)>=(k+1)/2)
printf("0\n");
else if(d&1)
printf("0\n");
else
printf("-1\n");
}
}
return 0;
}

【XSY2190】Alice and Bob VI 树形DP 树剖的更多相关文章

  1. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  2. 【HDU 5233】Tree chain problem (树形DP+树剖+线段树|树状数组)最大权不相交树链集

    [题目] Tree chain problem Problem Description Coco has a tree, whose vertices are conveniently labeled ...

  3. 树形DP+树状数组 HDU 5877 Weak Pair

    //树形DP+树状数组 HDU 5877 Weak Pair // 思路:用树状数组每次加k/a[i],每个节点ans+=Sum(a[i]) 表示每次加大于等于a[i]的值 // 这道题要离散化 #i ...

  4. [HDU 5293]Tree chain problem(树形dp+树链剖分)

    [HDU 5293]Tree chain problem(树形dp+树链剖分) 题面 在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大. 分析 考虑树形dp,dp[x]表示以x为子树 ...

  5. POJ 3162.Walking Race 树形dp 树的直径

    Walking Race Time Limit: 10000MS   Memory Limit: 131072K Total Submissions: 4123   Accepted: 1029 Ca ...

  6. HDU 2196.Computer 树形dp 树的直径

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  7. POJ 1655.Balancing Act 树形dp 树的重心

    Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14550   Accepted: 6173 De ...

  8. hdu 4607 树形dp 树的直径

    题目大意:给你n个点,n-1条边,将图连成一棵生成树,问你从任意点为起点,走k(k<=n)个点,至少需要走多少距离(每条边的距离是1): 思路:树形dp求树的直径r: a:若k<=r+1 ...

  9. computer(树形dp || 树的直径)

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

随机推荐

  1. koa服务器搭建基础

    之前我一直使用rails搭建网站.rails与koa的基本理念很相似,都是基于中间件提供一层层的服务.所不同的是,rails有很多内置的中间件,这使得开发者只需要关注MVC模块以及页面路由.而Koa这 ...

  2. HDU - 1754 线段树-单点修改+询问区间最大值

    这个也是线段树的经验问题,待修改的,动态询问区间的最大值,只需要每次更新的时候,去把利用子节点的信息进行修改即可以. 注意更新的时候区间的选择,需要对区间进行二分. #include<iostr ...

  3. codeforces#1090 D. New Year and the Permutation Concatenation(打表找规律)

    题意:给出一个n,生成n的所有全排列,将他们按顺序前后拼接在一起组成一个新的序列,问有多少个长度为n的连续的子序列和为(n+1)*n/2 题解:由于只有一个输入,第一感觉就是打表找规律,虽然表打出来了 ...

  4. AndroidManifest.xml文件解析

    一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(activiti ...

  5. Requires: libc.so.6(GLIBC_2.14)(64bit)

    centos6 - CentOS 6 - libc.so.6(GLIBC_2.14)(64bit) is needed by - Server Faulthttps://serverfault.com ...

  6. C# foreach内部原理

    我们知道使用foreach的一个要求是对象必须继承自IEnumerable接口 这样才可以进行迭代 那内部是怎么实现的呢 这个时候会将对应的foreach语句转换为一个while循环 并且通过Move ...

  7. Laravel Providers——服务提供者的注册与启动源码解析

      本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysishttps://learnku.com/a ...

  8. Java 里如何实现线程间通信(转载)

    出处:http://www.importnew.com/26850.html 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程 ...

  9. 理解根目录,classpath, getClass().getResourceAsStream和getClass().getClassLoader().getResourceAsStream的区别

    一: 理解根目录 <value>classpath*:/application.properties</value> <value>classpath:/appli ...

  10. [转帖]SAP一句话入门:Project System

    SAP一句话入门:Project System http://blog.vsharing.com/MilesForce/A621279.html 这是SAP ERP入门的最后一篇了. 我们这些死跑龙套 ...