题目描述
Zayin 和 Ziyin 正在玩有趣的捉迷藏游戏。
该游戏在一颗具有 n 个节点(编号从 11 到 n)的树上进行。
在游戏的开始,Zayin 在节点 a,而 Ziyin 在节点 b。他们轮流操作,Zayin 先移动。在每次移动中,Zayin 能移动到距离当前所在点不超过 da 的节点上,而 Ziyin 能移动到距离当前所在点不超过 db 的节点上(注意 可以保持在当前点不动)。
当某次移动后,其中一人抓住了另外一人,即移动到了另外一人的节点上,则游戏结束,被抓住的人输掉游戏。
当 Zayin 和 Ziyin 都按最优策略移动的话,谁会是最后赢家呢。
题目大意
给出一棵树,和两个人在树上的位置。两个人按能移动的距离轮流行动。如果一个人移动到了另一个人的位置上,则该人获胜,问谁会先到达对方的位置,也就是本题中要赢的条件。
结论
这道题实际上就是一道贪心。其实一个人是否可以先到达另一个人的位置,主要看的是这个人的移动步数是否能够大于对方。
因为对于一个点来说,它的距离大于对方,那么如果要退,那么对方一定追不上。如果要追,对方也一定逃不了。
但是有一种特殊情况:
对于先手,如果可以直接到达,则先手获胜。
注意
由于数据较大并且时间也够,所以这里使用倍增。时间复杂度为:O(NlogN)
Code:
first:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
#define lll __int128
#define wk(x) write(x),putchar(' ')
#define wh(x) write(x),putchar('\n')
#define r(x) read(x)
const long long M=2147483647;
const int N=1000005;
const int NM=1005;
using namespace std;
void read(int&);
void write(int);
int n,m,k,t,jk,tot,cnt,ans,sum,num,answer;
int a[N],f[NM][NM],dep[N],mx[N][105];
int head[N*2],to[N*2],nxt[N*2];
void add(int x,int y)
{
to[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
void dg(int x,int y,int de)
{
dep[x]=de;
for(register int i=head[x];i;i=nxt[i])
{
int v=to[i];
if(v==y) continue;
mx[v][0]=x;
dg(v,x,de+1);
}
}
int lca(int u,int v)
{
int pop=1,pop1=1;
if(dep[u]<dep[v]) swap(u,v);
for(register int i=22;i>=0;i--)
if(dep[mx[u][i]]>=dep[v]) u=mx[u][i];
if(u==v) return u;
for(register int i=22;i>=0;i--)
if(mx[u][i]!=mx[v][i]) u=mx[u][i],v=mx[v][i];
return mx[u][0];
}
int main()
{
freopen("game.in","r",stdin);//
freopen("game.out","w",stdout);//乱杀。
read(jk);read(jk);
while(jk--)
{
read(n),read(m);int x=0,y=0;
for(register int i=1;i<=n;i++)
{
head[i]=nxt[i]=to[i]=0;
dep[i]=0;
for(int j=1;j<=22;j++)
{
mx[i][j]=0;
}
}
cnt=0;
for(register int i=1;i<n;i++) r(x),r(y),add(x,y),add(y,x);//建边。
dg(1,0,1);
for(register int j=1;j<=22;j++)
for(register int i=1;i<=n;i++)
mx[i][j]=mx[mx[i][j-1]][j-1];//一个点的祖先是父亲的父亲。
for(register int i=1;i<=m;i++)
{
int da=0,db=0;
ans=2147483647;
r(x),r(y),r(da),r(db);
ans=lca(x,y);//判断。
ans=dep[x]+dep[y]-2*dep[ans];
if(ans<=da||da>db) printf("Zayin\n");
else
{
if(da<db) printf("Ziyin\n");
else printf("Draw\n");
}
}
}
return 0;
}
void read(int &x)
{
x=0;int ff=1;
char ch=getchar();
while (ch<'0'||ch>'9')
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
x*=ff;return;
}
void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
return;
}
second:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#define ll long long
#define lll __int128
#define wk(x) write(x),putchar(' ')
#define wh(x) write(x),putchar('\n')
#define r(x) read(x)
const long long M = 2147483647;
const int N = 1000005;
const int NM = 1005;
using namespace std;
void read(int&);
void write(int);
int n, m, k, t, jk, tot, cnt, ans, sum, num, answer;
int a[N], f[NM][NM], dep[N], mx[N][105];
int head[N * 2], to[N * 2], nxt[N * 2];
void add(int x, int y) {
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
}
void dg(int x, int y, int de) {
dep[x] = de;
for (register int i = head[x]; i; i = nxt[i]) {
int v = to[i];
if (v == y) continue;
mx[v][0] = x;
dg(v, x, de + 1);
}
}
int lca(int u, int v) {
int pop = 1, pop1 = 1;
if (dep[u] < dep[v]) swap(u, v);
for (register int i = 22; i >= 0; i--)
if (dep[mx[u][i]] >= dep[v]) u = mx[u][i];
if (u == v) return u;
for (register int i = 22; i >= 0; i--)
if (mx[u][i] != mx[v][i]) u = mx[u][i], v = mx[v][i];
return mx[u][0];
}
int main() {
freopen("game.in", "r", stdin); //
freopen("game.out", "w", stdout); //乱杀。
read(jk);
read(jk);
while (jk--) {
read(n), read(m);
int x = 0, y = 0;
for (register int i = 1; i <= n; i++) {
head[i] = nxt[i] = to[i] = 0;
dep[i] = 0;
for (int j = 1; j <= 22; j++) {
mx[i][j] = 0;
}
}
cnt = 0;
for (register int i = 1; i < n; i++) r(x), r(y), add(x, y), add(y, x); //建边。
dg(1, 0, 1);
for (register int j = 1; j <= 22; j++)
for (register int i = 1; i <= n; i++)
mx[i][j] = mx[mx[i][j - 1]][j - 1]; //一个点的祖先是父亲的父亲。
for (register int i = 1; i <= m; i++) {
int da = 0, db = 0;
ans = 2147483647;
r(x), r(y), r(da), r(db);
ans = lca(x, y); //判断。
ans = dep[x] + dep[y] - 2 * dep[ans];
if (ans <= da || da > db) printf("Zayin\n");
else {
if (da < db) printf("Ziyin\n");
else printf("Draw\n");
}
}
}
return 0;
}
void read(int &x) {
x = 0;
int ff = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') ff = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
x *= ff;
return;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}