[LOJ#531]「LibreOJ β Round #5」游戏

时间:2023-02-23 19:28:59

[LOJ#531]「LibreOJ β Round #5」游戏

试题描述

LCR 三分钟就解决了问题,她自信地输入了结果……

> …… 正在检查程序 ……

> …… 检查通过,正在评估智商 ……

> 对不起,您解决问题的速度过快,与加密者的智商不符。转入精确匹配。

> 由于您在模糊匹配阶段的智商差距过大,需要进行精确匹配。

LCR 发现,精确匹配是通过与随机对手(称为「神犇」)游戏的方式,藉由游戏的决策来评定智商的机制。游戏规则如下:

有一个长为 \(n\),下标为 \([1,n]\) 的数组 \(f[]\),且满足 \(f[i]\in [1,n]\)。

有一个变量 \(a\) 初始值为 \(1\)。双方轮流操作,LCR 先手。

操作方法:每次在所有满足 \(f[i]=a\) 的 \(i\) 中选一个,并将 \(a\) 赋值为 \(i\),不能不选。无法操作者输,若共 \(2n\) 次操作后仍未决出胜负,则为平局。

我们定义二元关系“到达”如下:

  • \(i\) 可以到达 \(i\)

  • \(i\) 可以到达 \(f[i]\)

  • 如果 \(i\) 能到达 \(j\),\(j\) 能到达 \(k\),则 \(i\) 能到达 \(k\)。

则 \(f\) 数组满足性质:对于任意 \(i,j\) 存在 \(k\) 使得 \(i\) 和 \(j\) 都能到达 \(k\)。

LCR 即将面对 \(q\) 局游戏。她发现每局游戏的 \(f[]\) 数组都和给定的「模板数组」很像。经过进一步研究她发现每局游戏可以描述如下:

给出两个整数 \(u,v\),满足在模板数组中 \(f[u]\) 能到达 \(u\),\(f[v]\) 能到达 \(v\)。则该局游戏的 \(f[]\) 是把模板数组的 \(f[u]\) 赋值为 \(v\) 后得到的。

现在 LCR 希望你帮她计算每局游戏的胜负状态。

输入

第一行两个正整数 \(n,q\)。

第二行 \(n\) 个整数表示 \(f[]\)。

接下来 \(q\) 行每行两个整数 \(u,v\) 描述一局游戏。

输出

输出共 \(q\) 行。

每行一个整数 \(r\) 表示结果。 \(r=1\) 表示先手(LCR)有必胜策略,\(r=0\) 表示后手(神犇)有必胜策略,\(r=2\) 表示平局。

输入示例

7 3
3 1 2 3 4 3 2
1 1
2 3
2 1

输出示例

2
0
0

数据规模及约定

对于所有数据,\(1\le n,q\le 10^6\)。

题解

这题贼恶心。。。

首先题目 BB 半天就是在说明如果建立一个有向图,满足 \(i\) 到 \(f[i]\) 有一条有向边,则这个有向图是一个环套树,其中树上所有节点都是指向环的。然后每次询问都是改变环上的一条边,改后边的终点还是在原环上。

那么分情况讨论吧。

(1) 如果 \(1\) 号节点在树上,直接树形 dp,每次询问都不影响结果。

(2) 如果 \(1\) 号点在环上,再分情况讨论一下修改。主要是两大类(显然改变后会形成一个新环):\(1\) 在改之后的新环上和不在新环上。首先我们从节点 \(1\) 顺着有向边给环依次标号,记原图中节点 \(x\) 的环标号为 \(cid[x]\),然后对于询问 \((u, v)\),我们令 \(U = cid[u]\),\(V = cid[v]\)(请记住一点,在博弈的时候是逆着原图方向走的,故以下“距离”等概念都是逆原图方向的)。

  1. \(1\) 在新环上,则要么 \(U < V\),要么 \(V = 1\)。在这里我们明确一点,就是在真正博弈的时候,双方都会逆着圆环走,直到碰到一个节点 \(node\),满足以 \(node\) 为根的外向树的 dp 结果为先手必胜,就会往树上走(此时我们可以认为走到节点 \(node\) 的那一方获胜);否则不会往树上走,如果不存在这样的 \(node\),那么就分不出输赢(因为双方会不停地在环上绕啊绕啊绕)。想清楚这一点,所有情况画画图都可以解决了。

  2. \(1\) 不在新环上,就是剩下的情况。

注意环形态的改变,会导致环上的一段被“甩”了出去,变成了树的一部分。这个时候我们需要重新处理一下树上点的 dp 值,手画一个样例,可以发现对于一段原 dp 值为 \(10001110010001\) 的环的一部分,在它断开之后,只有原来是 \(0\) 的部分的 dp 值可能改变,改变条件是前面有奇数个 \(0\)(可以想象从左往右有 \(dp[i] = dp[i]\texttt{ }or\texttt{ }not(dp[i-1])\),于是一段连续的 \(0\) 会变成 \(010101...\))。

还有就是我们需要维护一下距离环上每个节点最近的 dp 值为 \(1\) 的节点的位置(注意是逆着原图方向最近的),这样我们就能用距离的奇偶性出当移动到 dp 值为 \(1\) 的节点时是谁先手了。

我语言能力好差。。。看不懂的话丢链接跑。(标算方法可能和我的不太一样,目前(2017-10-13 23:09)我的做法最快)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std; const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
} #define maxn 1000010 int n, fa[maxn]; struct Graph {
int m, head[maxn], nxt[maxn], to[maxn];
Graph(): m(0) { memset(head, 0, sizeof(head)); }
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
} gr, tr; bool vis[maxn], is_cyc[maxn], has_tr[maxn], win[maxn];
int St[maxn], top, cps[maxn], cntc;
bool findcyc(int u) {
if(vis[u]) {
while(St[top] != u) is_cyc[cps[++cntc] = St[top--]] = 1;
is_cyc[cps[++cntc] = u] = 1;
return 1;
}
St[++top] = u; vis[u] = 1;
for(int e = gr.head[u]; e; e = gr.nxt[e]) if(findcyc(gr.to[e])) return 1;
return 0;
}
void dp(int u) {
win[u] = 0;
for(int e = tr.head[u]; e; e = tr.nxt[e]) if(!is_cyc[tr.to[e]]) {
has_tr[u] = 1;
dp(tr.to[e]);
if(!win[tr.to[e]]) win[u] = 1;
}
return ;
} int cid[maxn], pid[maxn], near[maxn];
bool winc[maxn], hastrc[maxn];
int Pre(int x) {
return x - 1 ? x - 1 : cntc;
}
int Nxt(int x) {
return x + 1 <= cntc ? x + 1 : 1;
}
int Dis(int a, int b) { // from a revers-cycle-steps to b
if(a >= b) return a - b;
else return a + cntc - b;
}
bool canwin(int a, int brk) {
if(!near[a]) return (a == brk ? cntc : Dis(a, brk)) - 1 & 1;
if(a == brk) return min(Dis(a, near[a]), cntc) - 1 & 1;
return min(Dis(a, near[a]), Dis(a, brk)) - 1 & 1;
} int main() {
n = read(); int q = read();
for(int i = 1; i <= n; i++) fa[i] = read(), tr.AddEdge(fa[i], i), gr.AddEdge(i, fa[i]); findcyc(1);
// printf("cps: "); for(int i = 1; i <= cntc; i++) printf("%d%c", cps[i], i < cntc ? ' ' : '\n');
bool oncyc = 0;
for(int i = 1; i <= cntc; i++) {
dp(cps[i]);
if(cps[i] == 1) oncyc = 1;
}
if(!oncyc) {
while(q--) {
read(); read();
printf("%d\n", win[1]);
}
return 0;
}
// remark id
int onep, cnt = 0, ntr = -1;
for(int i = cntc; i; i--) if(cps[i] == 1){ onep = i; break; }
for(int i = onep; i; i--) cid[cps[i]] = ++cnt, pid[cnt] = cps[i], winc[cnt] = win[cps[i]], hastrc[cnt] = has_tr[cps[i]];
for(int i = cntc; i > onep; i--) cid[cps[i]] = ++cnt, pid[cnt] = cps[i], winc[cnt] = win[cps[i]], hastrc[cnt] = has_tr[cps[i]];
// for(int i = 1; i <= cntc; i++) printf("%d: %d %d\n", i, hastrc[i], winc[i]);
for(int i = 1; ; i = Nxt(i)) {
if(hastrc[i] && winc[i]) ntr = i;
if(ntr >= 0) near[i] = ntr;
if(Nxt(i) == 1) break;
}
for(int i = 1; ; i = Nxt(i)) {
if(hastrc[i] && winc[i]) ntr = i;
if(ntr >= 0) near[i] = ntr;
if(Nxt(i) == 1) break;
}
// for(int i = 1; i <= cntc; i++) if(hastrc[i] && winc[i]) printf("(1)%d ", i); putchar('\n');
for(int kase = 1; kase <= q; kase++) {
int u = read(), v = read();
u = cid[u]; v = cid[v];
if(u < v) {
if(winc[1]){ puts("1"); continue; }
if(near[1] >= v){ puts((Dis(1, near[1]) & 1) ? "0" : "1"); continue; }
if(winc[v] | canwin(v, u)){ puts((Dis(1, v) & 1) ? "0" : "1"); continue; }
if(1 < near[u] && near[u] <= u){ puts((Dis(1, v) + 1 + Dis(u, near[u]) & 1) ? "0" : "1"); continue; }
puts("2");
continue;
}
if(v == 1) {
if(winc[1] | canwin(1, u)) puts("1");
else if(1 < near[u] && near[u] <= u) puts((Dis(u, near[u]) + 1 & 1) ? "0" : "1");
else puts("2");
continue;
}
if(winc[1] | canwin(1, u)) puts("1");
else puts("0");
} return 0;
}

[LOJ#531]「LibreOJ β Round #5」游戏的更多相关文章

  1. &lbrack;LOJ&num;530&rsqb;「LibreOJ β Round &num;5」最小倍数

    [LOJ#530]「LibreOJ β Round #5」最小倍数 试题描述 第二天,LCR 终于启动了备份存储器,准备上传数据时,却没有找到熟悉的文件资源,取而代之的是而屏幕上显示的一段话: 您的文 ...

  2. &lbrack;LOJ&num;516&rsqb;「LibreOJ β Round &num;2」DP 一般看规律

    [LOJ#516]「LibreOJ β Round #2」DP 一般看规律 试题描述 给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作. 每次操作的内容为:给定 \(x,y\ ...

  3. &lbrack;LOJ&num;515&rsqb;「LibreOJ β Round &num;2」贪心只能过样例

    [LOJ#515]「LibreOJ β Round #2」贪心只能过样例 试题描述 一共有 \(n\) 个数,第 \(i\) 个数 \(x_i\) 可以取 \([a_i , b_i]\) 中任意值. ...

  4. &lbrack;LOJ&num;525&rsqb;「LibreOJ β Round &num;4」多项式

    [LOJ#525]「LibreOJ β Round #4」多项式 试题描述 给定一个正整数 k,你需要寻找一个系数均为 0 到 k−1 之间的非零多项式 f(x),满足对于任意整数 x 均有 f(x) ...

  5. &lbrack;LOJ&num;526&rsqb;「LibreOJ β Round &num;4」子集

    [LOJ#526]「LibreOJ β Round #4」子集 试题描述 qmqmqm有一个长为 n 的数列 a1,a2,……,an,你需要选择集合{1,2,……,n}的一个子集,使得这个子集中任意两 ...

  6. &lbrack;LOJ&num;522&rsqb;「LibreOJ β Round &num;3」绯色 IOI(危机)

    [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机) 试题描述 IOI 的比赛开始了.Jsp 和 Rlc 坐在一个角落,这时他们听到了一个异样的声音 …… 接着他们发现自己收 ...

  7. LibreOJ &num;524&period; 「LibreOJ β Round &num;4」游戏

    二次联通门 : LibreOJ #524. 「LibreOJ β Round #4」游戏 /* LibreOJ #524. 「LibreOJ β Round #4」游戏 找找规律就会发现.. 当有X的 ...

  8. loj &num;547&period; 「LibreOJ β Round &num;7」匹配字符串

    #547. 「LibreOJ β Round #7」匹配字符串   题目描述 对于一个 01 串(即由字符 0 和 1 组成的字符串)sss,我们称 sss 合法,当且仅当串 sss 的任意一个长度为 ...

  9. loj &num;535&period; 「LibreOJ Round &num;6」花火 树状数组求逆序对&plus;主席树二维数点&plus;整体二分

    $ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...

随机推荐

  1. Oracle 11g XE release2安装与指导

    今天上午我安装了Oracle 11g企业版,发现太占内存了,考虑到MS SQL有express版本,所以寻思着尝试尝试Oracle 11g的express版本,就是EX版本.下面是具体的安装步骤. 1 ...

  2. Eplan转载

    引言:标准化工程设计理念成功实施后之后,清晰透明的管理流程将水到渠成,过往繁复无比的流程得以简化,管理与被管理者皆愿欣然承受. 市场竞争日趋激烈的今天,对用户需求.市场的快速响应,尽量地控制成本是企业 ...

  3. 【干货】Laravel --实战篇 UUID&lpar;唯一识别码&rpar;

    前言 : 一般的唯一识别id都是各种时间戳.毫秒级时间戳加php内置函数或者加上随机数等手段来生成的. 下面给大家介绍一个组件,也是我在各个实战项目中必不可少的一个组件,ramsey/uuid.一.r ...

  4. Lua利用cjson读写json示例分享

    本文结合本人的实际使用经验和代码示例,介绍如何在Lua中对json进行encode和decode,需要的朋友可以参考下 我这里采用的是Lua CJson库,是一个高性能的JSON解析器和编码器,其性能 ...

  5. Linux 守护进程的启动方法

    守护进程”(daemon)就是一直在后台运行的进程(daemon). 本文介绍如何将一个 Web 应用,启动为守护进程. 一.问题的由来 Web应用写好后,下一件事就是启动,让它一直在后台运行. 这并 ...

  6. Redis持久化的两种方式(RDB和AOF)

    redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File). RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储 ...

  7. linux 下将tomcat注册成服务并开机启动

    一.将startup.sh和shutdown.sh新建软连接到/usr/bin ln -s /usr/local/apache-tomcat-8.5.38/bin/startup.sh /usr/bi ...

  8. springboot整合zookeeper

    在springboot中所有的整合都是以bean的形式注入对象,从数据库coon.redis conn.再到整合的zookeeper,依然是依照bean注入连接对象,通过zookeeper api对z ...

  9. Git使用01

    git 工作区:当前编辑的区域 缓存区:add 之后的区域 本地仓库:commit之后的区域 远程仓库:远程的区域 git init 初始化 git status 查看git的状态 git add 将 ...

  10. CSS3 transition实现超酷图片墙动画效果

    一.前面的感慨以前也陆陆续续试过CSS3的一些特性,文字投影,多边框等.但都是试试而已,知道有这么回事.今天,见到了一个新玩意,transition,认认真真的试了一下,经过,我懵了,我呆了,我傻了, ...