魔术帽游戏 [倍增_隐 III]


S o l u t i o n \mathcal{Solution} Solution

建图过程:

交换操作 可以看作魔术球在 帽子序列 中的 移动,
使用每个魔术帽的 位置 为 编号 建图,
操作序列中的 a , b a, b a,b 连两条 有向边, 分别为 a a a -> b b b a a a <- b b b, 边权 为当前 操作序号

带有小球的帽子交换一次, 表示小球走过一条边,
设该边 编号 i i i, 到达了 k k k 点, 则小球的下一次移动必定会经过满足以下条件的边 ↓ ↓

  1. k k k 相连的
  2. 边的 权值 大于 i i i 的 所有边 中 最小的那一条

对第二个条件的解释: 操作顺序在 i i i 后, 继 i i i 后 下一次 直接 改变小球的 位置 的操作 所代表的边.

对于加速该操作, 可以使用如代码中所示 F k 1 [ i , 0 ] = n x t [ i   异 或   1 ] Fk_1[i, 0]=nxt[i\ 异或 \ 1] Fk1[i,0]=nxt[i  1], 然而这样会导致不可预料的 b u g bug bug (如最下方代码), 所以要谨慎使用

于是在一个固定的操作序列中, 即固定的中, 小球的起点边一旦确定, 其路径也会随之固定,
确认了这一点之后, 使用倍增加速 ↓ ↓

  1. 从魔术球所在的帽子所 连出 的边中寻找 l l l 操作
  2. 魔术球从 l l l r r r 的移动

C o d e \mathcal{Code} Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 2000005;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

struct Edge{ int nxt, to, w; } edge[maxn << 1];

int N;
int M;
int Q;
int num0;
int a[maxn];
int b[maxn];
int head[maxn];
int Fk_1[maxn][20];
int Fk_2[maxn][20];

void Add(int from, int to, int w){
        edge[++ num0] = (Edge){ head[from], to, w };
        head[from] = num0;
}

int main(){
        freopen("magic.in", "r", stdin);
        freopen("magic.out", "w", stdout);
        N = read();
        M = read();
        Q = read();
        for(reg int i = 1; i <= M; i ++) a[i] = read(), b[i] = read();
        num0 = 1, edge[0].w = 0x7f7f7f7f;
        for(reg int i = M; i >= 1; i --) Add(a[i], b[i], i), Add(b[i], a[i], i);
        for(reg int i = 2; i <= num0; i ++){
                Fk_1[i][0] = edge[i^1].nxt;
                Fk_2[i][0] = edge[i].nxt;
        } 
        for(reg int j = 1; j <= 19; j ++)
                for(reg int i = 2; i <= num0; i ++){
                        Fk_1[i][j] = Fk_1[Fk_1[i][j-1]][j-1]; 
                        Fk_2[i][j] = Fk_2[Fk_2[i][j-1]][j-1];
                }
        for(reg int i = 1; i <= Q; i ++){
                int x = read();
                int l = read();
                int r = read();
                int t = head[x];
                for(reg int i = 19; i >= 0; i --)
                        if(edge[Fk_2[t][i]].w < l) t = Fk_2[t][i];
                if(edge[t].w < l) t = Fk_2[t][0];
                t = edge[t].w>r?0:t;
                for(reg int i = 19; i >= 0; i --)
                        if(edge[Fk_1[t][i]].w <= r) t = Fk_1[t][i];
                printf("%d\n", edge[t].to?edge[t].to:x);
        }
        return 0;
}


C o d e   w i t h   b u g \mathcal{Code\ with\ bug} Code with bug

为什么以 0 0 0 为第一条边就 W A WA WA 掉了…

#include<bits/stdc++.h>
#define reg register

const int maxn = 2000005;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

struct Edge{ int nxt, to, w; } edge[maxn << 1];

int N;
int M;
int Q;
int num0;
int a[maxn];
int b[maxn];
int head[maxn];
int Fk_1[maxn][22];
int Fk_2[maxn][22];

void Add(int from, int to, int w){
        edge[num0] = (Edge){ head[from], to, w };
        head[from] = num0 ++;
}

int main(){
        freopen("magic.in", "r", stdin);
        freopen("magic.out", "w", stdout);
        N = read();
        M = read();
        Q = read();
        for(reg int i = 1; i <= M; i ++) a[i] = read(), b[i] = read();
        memset(head, -1, sizeof head);
        for(reg int i = M; i >= 1; i --) Add(a[i], b[i], i), Add(b[i], a[i], i);
        for(reg int i = 0; i < num0; i ++){
                Fk_1[i][0] = edge[i^1].nxt;
                Fk_2[i][0] = edge[i].nxt;
        } 
        for(reg int j = 1; j <= 20; j ++)
                for(reg int i = 0; i <= num0; i ++){
                        Fk_1[i][j] = Fk_1[Fk_1[i][j-1]][j-1]; 
                        Fk_2[i][j] = Fk_2[Fk_2[i][j-1]][j-1];
                }
        for(reg int i = 1; i <= Q; i ++){
                int x = read();
                int l = read();
                int r = read();
                int t = head[x];

                if(t == -1){ printf("%d\n", x); continue ; }

                for(reg int i = 20; i >= 0; i --){
                        if(Fk_2[t][i] == -1) continue ;
                        if(edge[Fk_2[t][i]].w < l) t = Fk_2[t][i];
                }

                if(edge[t].w < l) t = Fk_2[t][0];

                if(t == -1 || edge[t].w > r){ printf("%d\n", x); continue ; }

                for(reg int i = 20; i >= 0; i --){
                        if(Fk_1[t][i] == -1) continue ;
                        if(edge[Fk_1[t][i]].w <= r) t = Fk_1[t][i];
                }

                printf("%d\n", edge[t].to?edge[t].to:x);

        }
        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值