LG P5473 [NOI2019] I 君的探险

Description

时隔半年,I 君的商店终于开不下去了,他决定转让商店,做一名探险家去探索未知的广阔世界。

根据古书记载,他在一个大荒漠的腹地找到了未知文明创造的地下宫殿,宫殿由 $N$ 个大型洞穴和 $M$ 条连接这些洞穴的双向通路构成。I 君能借助古书分辨所处的洞穴,但书中并没有记录 $M$ 条通路的连接结构,因此他难以搜寻传说中藏在宫殿里的无尽财宝。

不过现在 I 君发现了一个神秘机关,通过它可以获知宫殿的信息,I 君决定利用这个机关来得到宫殿的连接结构,请你来协助他。

地下宫殿可以抽象成一张 $N$ 个点、$M$ 条边的无向简单图(简单图满足任意两点之间至多存在一条直接相连的边),洞穴从 $0 \sim n - 1$ 编号。目前你并不知道边有哪些。

每个洞穴都拥有一个光源,光源有开启、关闭两种状态,只有当光源处于开启状态时它所在的洞穴才会被照亮。初始时所有的光源都处于关闭状态,而光源的状态只能用I 君发现的神秘机关改变。更具体的,使用神秘机关可以进行如下四种操作:

1. 向机关给定一个编号 $x$,机关将会改变$x$ 号洞穴,以及与$x$ 号洞穴有通路直接相连的洞穴的光源状态。即原来开启的光源将会关闭;原来关闭的光源将会开启。

2. 向机关给定一个编号 $x$,机关将会显示当前$x$ 号洞穴光源的状态。

3. 向机关给定两个编号 $x, y$,表示你确定有一条连接 $x$ 号洞穴与 $y$ 号洞穴的通路,并让机关记录。

4. 向机关给定一个编号 $x$,机关将会判断与 $x$ 号洞穴相连的通路是否都已被记录。

机关在完成上一次操作后才能进行下一次操作。机关不能随意使用,因此每种操作的使用次数都有限制,分别为 $L_m, L_q, M, L_c$。你的任务是,编写一个程序,帮助 I 君决定如何合理利用神秘机关,从而正确地找到这 $M$ 条通路。

Solution

需要写多个部分分

第一个部分分:每次改变某个点的状态,枚举其它点状态是否变化,若变化则有连边

第三个部分分:保证数据为一棵树,整体二分,每次改变$[l,mid]$的状态,如果询问范围中的点满足$\leq mid$或状态改变则连边在分治范围左侧

第四个部分分:保证数据为一条链,二进制分组,每次改变二进制位某一位为1的所有点的状态,如果询问范围中的点只满足该位为1或状态改变中的一个,那么它相连的两个点的该位上的异或和为1,暴力查找0点所连的边,由0点开始向链的两侧扩展更新答案

第六个部分分:类似第三个部分分,这次需要在一个随机的询问序列上进行整体二分,可以连好某个排列里向前连边是奇数的点,重复做几次即可

其余的部分分可以归为第六个部分分

#include "explore.h"
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
using namespace std;
void modify(int x);
int query(int x);
void report(int x,int y);
int check(int x);
namespace task1{
    int sta[505];
    void main(int n){
        for(int i=0;i<=n-2;i++){
            modify(i),sta[i]^=1;
            for(int j=i+1;j<=n-1;j++)if(sta[j]^query(j))sta[j]^=1,report(i,j);
        }
    }
}
namespace task3{
    int q[200005],q1[200005],q2[200005];
    void solve(int l,int r,int ql,int qr){
        if(ql>qr)return;
        if(l==r){
            for(int i=ql;i<=qr;i++)report(l-1,q[i]-1);
            return;
        }
        int mid=l+r>>1,tot1=0,tot2=0;
        for(int i=l;i<=mid;i++)modify(i-1);
        for(int i=ql;i<=qr;i++)
            if(q[i]<=mid||query(q[i]-1))q1[++tot1]=q[i];
            else q2[++tot2]=q[i];
        for(int i=l;i<=mid;i++)modify(i-1);
        for(int i=1;i<=tot1;i++)q[ql+i-1]=q1[i];
        for(int i=1;i<=tot2;i++)q[ql+tot1+i-1]=q2[i];
        solve(l,mid,ql,ql+tot1-1),solve(mid+1,r,ql+tot1,qr);
    }
    void main(int n){
        report(0,1);
        for(int i=3;i<=n;i++)q[i]=i;
        solve(1,n,3,n);
    }
}
namespace task4{
    int p[200005];
    void dfs(int k,int fa){
        if(!(fa^p[k]))return;
        report(k,fa^p[k]),dfs(fa^p[k],k);
    }
    void main(int n){
        for(int i=0;(1<<i)<n;i++){
            for(int j=0;j<n;j++)if(j>>i&1)modify(j);
            for(int j=0;j<n;j++)if((j>>i&1)^query(j))p[j]|=1<<i;
            for(int j=0;j<n;j++)if(j>>i&1)modify(j);
        }
        modify(0);
        for(int i=1;i<n;i++)if(query(i))report(0,i),dfs(i,0);
    }
}
namespace task6{
    int cnt,tot,id[200005],q[200005],sta[200005],q1[200005],q2[200005];
    bool vst[200005];
    vector<int>ve[200005];
    bool calc(int x){
        bool ret=query(id[x]-1);
        for(int i=0;i<ve[id[x]].size();i++)ret^=sta[ve[id[x]][i]];
        return ret;
    }
    void solve(int l,int r,int ql,int qr){
        if(ql>qr)return;
        if(l==r){
            for(int i=ql;i<=qr;i++)if(q[i]!=l)report(id[l]-1,id[q[i]]-1),ve[id[l]].push_back(id[q[i]]),ve[id[q[i]]].push_back(id[l]),--cnt;
            return;
        }
        int mid=l+r>>1;
        for(int i=l;i<=mid;i++)if(!vst[id[i]])modify(id[i]-1),sta[id[i]]=1;
        int tot1=0,tot2=0;
        for(int i=ql;i<=qr;i++)
            if(q[i]<=mid||calc(q[i]))q1[++tot1]=q[i];
            else q2[++tot2]=q[i];
        for(int i=l;i<=mid;i++)if(!vst[id[i]])modify(id[i]-1),sta[id[i]]=0;
        for(int i=1;i<=tot1;i++)q[ql+i-1]=q1[i];
        for(int i=1;i<=tot2;i++)q[ql+tot1+i-1]=q2[i];
        solve(l,mid,ql,ql+tot1-1),solve(mid+1,r,ql+tot1,qr);
    }
    void main(int n,int m){
        cnt=m;
        for(int i=1;i<=n;i++)id[i]=i;
        while(cnt){
            random_shuffle(id+1,id+n+1),tot=0;
            for(int i=1;i<=n;i++)if(!vst[id[i]])q[++tot]=i;
            solve(1,n,1,tot);
            if(cnt)for(int i=1;i<=n;i++)if(!vst[i]&&check(i-1))vst[i]=true;
        }
    }
}
void explore(int n,int m){
    if(n<=500)task1::main(n);
    else if(n%10==7)task3::main(n);
    else if(n%10==6)task4::main(n);
    else task6::main(n,m);
}
[NOI2019] I 君的探险

 

posted @ 2021-01-08 11:02  QDK_Storm  阅读(205)  评论(0编辑  收藏  举报