[GDKOI2024 普及组] 捉迷藏

题目描述

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;
}

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值