嘘~ 正在从服务器偷取页面 . . .

CF1142E Pink Floyd 题解


CF1142E Pink Floyd 题解

题目链接:CF1142E Pink Floyd

题意

有一张 \(n\) 个点的竞赛图 \(G\),其中有 \(m\) 条边是粉色的,剩下 \(\binom {n}{2} - m\) 条边都是绿色的。你现在已经知道所有 \(m\) 条粉色边及其定向,但你不知道绿色边的方向。

你可以向交互库发出不超过 \(2 n\) 次询问,每次询问的内容为 " 询问一条绿色边的方向 "。在询问完毕后,你需要找到一个点 \(u\),使得:对于 \(G\) 中的任何一个点 \(v\),均存在一条从 \(u\)\(v\) 的 (有向) 路径,满足路径上的所有边同色

可以证明,对于任何一张竞赛图和任意的边染色方案,这样的点 \(v\) 是一定存在的。

输入格式

第一行包含两个正整数 \(n, m (1 \leq n \leq 10^5,~0 \leq m \leq \min \left\{ \binom n2, 10^5 \right\})\),表示竞赛图的点数和粉色边的条数。

接下来的 \(m\) 行,每行两个正整数 \(u, v~(1 \leq u, v \leq n)\),描述一条粉色的边。

输出格式

如果你需要询问边 \((u, v)\) 的方向,首先需要保证不存在连接 \(u\)\(v\) 的粉色边,然后你向标准输出打印 ? u v 并刷新缓存。最后从标准输出中读取一个整数 \(x\),如果 \(x = 1\) 表示定向为 \(u \to v\),如果 \(x = 0\) 表示定向为 \(v \to u\)

当你已经确定找到满足条件的点 \(v\) 时,输出 ! v 并退出程序即可。

\(\mathcal{Part\ 1}\)

很有意思的图论题。评分高应该是因为前面的题难?

题目就是要在 \(2n\) 次的询问内找出原图的一棵树形图。

因为这是一张竞赛图,所以根据淘汰赛选拔的常识其实就可以想到这题的正解。


\(\mathcal{Part\ 2}\)

竞赛图,之所以叫竞赛图而不叫吊打图,就是因为可以存在强连通分量

这个强连通分量也不是不能理解。可以当作 Minecraft 职业战争,不同职业均有被克的关系。

注意到这个树形图可以多解(所以搞了个自适应交互库,不然不好卡是吧

因此在竞赛图中找出一个树形图,就相当于我们只关心某种钦定顺序下的“吊打关系”。

这么想想也是合理的,毕竟不同职业也没有个最强最弱,能不能赢更多的是靠运气(随机的比赛顺序)

相信玩过这种职业类 PVP 游戏的 OIer 应该都能轻松理解吧(


\(\mathcal{Part\ 3}\)

首先考虑没有粉色边的情况。

我们可以把所有人先拉出来,然后两两比赛,留下胜者。最后只有一个人的时候它就是那个树形图的根。

形式化地,我们维护一个集合 \(S\) 表示当前还不在树上的点,每次随便取出两个结点 \(u,v\)

然后询问 \((u,v)\) 的方向,如果是 \(u \to v\) ,就把 \(v\) 踢掉,否则踢掉 \(u\)

接着考虑有粉色边的情况。

对于粉色边构成的强连通分量,根据强连通分量的性质,一定存在树形图。

一个简单的想法直接把原图(粉色边构成的图)变成一个 DAG 。

这里其实并不适用缩点,因为刚才说了,如果 \(u\) 吊打 \(v\)\(v\) 吊打 \(k\) ,不代表 \(u\) 吊打 \(k\)

那么怎么做呢?


\(\mathcal{Part\ 4}\)

考虑这样一个算法

  1. 把所有入度为 \(0\) 的点加入 \(S\) (仅考虑粉色边对度数的贡献)

  2. 每次还是取出两个结点 \(u,v\) ,把 \((u,v)\) 定向,不妨设 \(u\) 被留下

  3. 接着把所有 \(u\) 的粉色出边都删掉,并把此时入度为 \(0\) 的点加入 \(S\)

  4. 重复定向(操作2,3)直到只剩一个结点。

正确性很显然(但是其实并不好证明,证明放在文章最后,来自参考文献[1])

通俗地说,因为每个不在 DAG 上的点都可以被绿色边跑到,所以不会漏掉结点。

而这个删边的过程相当于在那个 DAG 上拓扑,顺便把其他的点给加上去。

时间复杂度 \(\mathcal{O}(n+m)\) ,询问次数只有不超过 \(n-1\) 次。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
namespace FastIO
{
    #define gc() getchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N ((int)(1e5+15))


bitset<N> vis,ins;
int n,m,pos=1,head[N],in[N];
struct Edge{ int u,v,next; } e[N * 2];
vector<int> to[N],now;
int ask(int u,int v)
{
    static int t;
    printf("? %lld %lld\n",u,v);
    fflush(stdout); read(t); return t;
}
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
void Tarjan(int u)
{
    vis[u] = ins[u] = 1;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].v;
        if(!ins[v]){ to[u].push_back(v); ++in[v]; }
        if(!vis[v]) Tarjan(v);
    }
    ins[u] = 0;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n); read(m);
    for(int i=1,u,v; i<=m; i++)
    { read(u); read(v); addEdge(u,v); }
    for(int i=1; i<=n; i++) if(!vis[i]) Tarjan(i);
    for(int i=1; i<=n; i++) if(!in[i]) now.push_back(i);
    for(int u,v; now.size() > 1; )
    {
        u = now.back(); now.pop_back();
        v = now.back(); now.pop_back();
        if(!ask(u,v)) swap(u,v); now.push_back(u);
        for(auto i : to[v]) if(!(--in[i])) now.push_back(i);
    }
    printf("! %lld\n", now.front());
    return 0;
}

\(\mathcal{Part\ 5}\)

算法正确性证明:


参考文献

[1] https://yhx-12243.github.io/OI-transit/?redirect=591


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录