P11832 [省选联考 2025] 图排列 题解

· · 题解

感觉挺有意思的。

首先我们可以对于 G 的每一个连通块分别求解答案,最后再考虑整合到一起。

首先来考虑树的情况,对于给定的一棵树 T,不妨假设其节点编号为 1 \sim n

我们首先令 p_1=i,假设 i 的儿子节点按照某个顺序排列x_1,x_2,x_3\dots x_k

那么我们划分 p_{2 \sim siz_{x_1}+1} 这一段分配给 x_1 子树内的点,p_{siz_{x_1}+2 \sim siz_{x_1}+siz_{x_2}+1}x_2 这一棵子树,以此类推。

如果不这样划分那么肯定会出现 a_i<a_j<b_i<b_j

继续观察这样划分会有什么性质,以 x_1 所在段为例,假设 x_1 的儿子按照某个顺序排列y_1,y_2,y_3\dots y_t,那么分配方案和上面的方法是类似的,唯一需要的考虑的是 x_1 放在哪里,而且 x 显然不能放在某个 y_j 分配的区间内,不然 (1,x_1)y_j 分配的段之间的边肯定会相交。

所以我们可以选择一个 l,使得分配了 y_1,y_2\dots y_l 之后将 x_1 放在此处再继续分配 y_{l+1},y_{l+2}\dots y_t

上述的方法肯定能够合法排布。

也就是说我们可以令 i=1 此时肯定也有解,分析一下不难得到我们直接按照子树编号最小值排布儿子肯定是最优的

这样我们就可以构造出一棵树的解。

现在有了环该怎么办呢?

如果有一个环 r_1,r_2,r_3\dots r_k,其中 (r_i,r_{i\bmod k+1}) 存在。

我们令这一个环上在排列 p 中位置最靠前的为 r_1,那么接下来容易知道 r_2,r_3\dots r_k 的相对大小关系只有两种可能 pos_{r_2}<pos_{r_3}<pos_{r_4}<\dots < pos_{r_k} 或者 pos_{r_2}>pos_{r_3}>pos_{r_4}>\dots >pos_{r_k}

所以我们将所有极大的简单环缩在一起,也就是跑点双连通分量。

然后考虑建圆方树,有了树的结构之后我们考虑修改一下树的做法。

首先对于每一个圆点而言,其所有儿子都是代表一个点双,这些点双的顺序和树的分析一样,可以任意排列,那么我们肯定取这一个点双中 能取到的最小的 第一个值来排序。

但是对于一个方点而言,儿子的顺序并不能随意排列,因为上面我们说了对于一个环其顺序只有两种可能,又因为这题保证有解,所以一个点双中不可能有两个不同的简单环。

证明如下

"\otimes" 指将环上的点排布成一个圆,将所有边画在圆内,其内部存在两条边视觉上如此排布。

如果环中没有 "\otimes" 的结构,那么我们直接通过缩二度点就可以知道简单环是唯一的。

如果有 "\otimes" 这样的边出现,此时无论怎么排布环上的点边都肯定出现 a_i<a_j<b_i<b_j

通过缩二度点得到了每一个点双的简单环之后和树的做法就没有太大差别了。

连通块的整合也是容易的,一个连通块可以整体一起插入到某一个位置,处理出每一个连通块的答案之后贪心即可。

//Mashiro
#include<bits/stdc++.h>

#define vc vector
#define db double
#define fi first
#define se second
#define ll long long
#define mk make_pair
#define pb push_back
#define RI register int
#define PI pair<int,int>
#define ull unsigned long long
#define err cerr << "   -_-   " << endl
#define debug cerr << " ------------------- " << endl

#define input(x) freopen(#x".in","r",stdin)
#define output(x) freopen(#x".out","w",stdout)

#define NO puts("No")
#define YES puts("Yes")

//#define OccDreamer
//#define int long long

using namespace std;

namespace IO{
    inline ll read(){
        ll X=0, W=0; char ch=getchar();
        while(!isdigit(ch)) W|=ch=='-', ch=getchar();
        while(isdigit(ch)) X=(X<<1)+(X<<3)+(ch^48), ch=getchar();
        return W?-X:X;
    }
    inline void write(ll x){
        if(x<0) x=-x, putchar('-');
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline void sprint(ll x){write(x), putchar(32);}
    inline void eprint(ll x){write(x), putchar(10);}
}using namespace IO;

const int MAXN = 2e5 + 2;

int n, m;
int stk[MAXN], fa[MAXN], topf;
int low[MAXN], dfn[MAXN], tim[MAXN], tot;
int head[MAXN], ne[MAXN<<1], to[MAXN<<1], mn[MAXN], node, cnt;

vc<PI> ed[MAXN];
vc<int> ans, G[MAXN], res[MAXN];

priority_queue<int> Q;

bool vis[MAXN];

inline bool comp(int x, int y){return mn[x]<mn[y];}

inline void add(int x, int y){++cnt;to[cnt]=y;ne[cnt]=head[x];head[x]=cnt;}

inline void tarjan(int x, int f){
    dfn[x]=low[x]=++tot; stk[++topf]=x;
    for(int i=head[x];i;i=ne[i]){
        if(dfn[to[i]]){
            if(to[i]^f) low[x]=min(low[x],dfn[to[i]]);
        }
        else{
            tarjan(to[i],x), low[x]=min(low[x],low[to[i]]);
            if(low[to[i]]>=dfn[x]){
                ++node; G[node].clear(); ed[node].clear();
                G[x].pb(node); fa[node]=x; stk[topf+1]=0;
                while(stk[topf+1]!=to[i]){
                    int t=stk[topf];
                    G[node].pb(t); fa[t]=node;
                    --topf;
                }
            }
        }
    }
    return ;
}

inline void build(int x, int id, int f){
    if(x^f) G[id].pb(x); vis[x]=1;
    for(int i=head[x];i;i=ne[i]){
        if(vis[to[i]]) continue;
        build(to[i],id,f);
    }
    return ;
}

set<int> e[MAXN];
set<PI> E, ban;

queue<int> q;

int deg[MAXN];

inline void Run(int lim){
    E.clear(); ban.clear();
    while(q.size()){
        int t=q.front(); q.pop();
        if(e[t].size()==1 && E.size()<lim){
            int x=*e[t].begin();
            E.insert(mk(min(t,x),max(t,x)));
        }
        if(e[t].size()==2){
            int x=*e[t].begin(), y=*e[t].rbegin();
            e[x].insert(y);
            e[y].insert(x);
            ban.insert(mk(min(x,y),max(x,y)));
        }
        for(auto i:e[t]){
            e[i].erase(e[i].lower_bound(t));
            if(ban.find(mk(min(t,i),max(t,i)))==ban.end()){
                E.insert(mk(min(t,i),max(t,i)));
            }
            if(e[i].size()==2) q.push(i);
        }
        e[t].clear();
    }
    for(auto i:E) add(i.fi,i.se), add(i.se,i.fi);
    return ;
}

inline void rebuild(int x){
    int cntt=0;
    for(auto i:G[x]) ++cntt, vis[i]=0, head[i]=deg[i]=0; vis[fa[x]]=0; head[fa[x]]=deg[fa[x]]=0;
    for(auto i:ed[x]) e[i.fi].insert(i.se), e[i.se].insert(i.fi), deg[i.fi]++, deg[i.se]++;
    for(auto i:G[x]) if(deg[i]<=2) q.push(i);
    if(deg[fa[x]]<=2) q.push(fa[x]);
    G[x].clear(); Run(cntt);
    return build(fa[x],x,fa[x]);
}

inline void DFS(int x, int f){
    int minn=(x<=n?x:0);
    for(auto i:G[x])
        DFS(i,x), minn=min(minn,mn[i]);
    if(x<=n){
        G[x].pb(x); mn[x]=x;
        sort(G[x].begin(),G[x].end(),comp);
        mn[x]=minn;
    }
    else{
        rebuild(x);
        if(mn[G[x][0]]>mn[G[x].back()]) reverse(G[x].begin(),G[x].end());
        mn[x]=mn[G[x][0]];
    }
    return ;
}

inline void getres(int x, int id){
    vis[x]=1;
    for(auto i:G[x]){
        if(i==x) res[id].pb(x);
        else getres(i,id);
    }
    return ;
}

inline void solve(int x){
    DFS(x,0);
    getres(x,x);
    return ;
}

inline void work(int x){
    for(auto i:res[x]){
        int lmt=Q.empty()?n+1:-Q.top();
        while(lmt<i){
            Q.pop();
            work(lmt);
            lmt=Q.empty()?n+1:-Q.top();
        }
        sprint(i);
    }
    return ;
}

inline void solve(){
    node=n=read(), m=read(); cnt=tot=topf=0;
    for(int i=1;i<=n;++i) G[i].clear(), head[i]=dfn[i]=0;
    for(int i=1;i<=m;++i){
        int x, y;
        x=read(), y=read();
        add(x,y), add(y,x);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i,fa[i]=0); 
    for(int i=1;i<=n;++i){
        for(int j=head[i];j;j=ne[j]){
            if(to[j]<i){
                int x=to[j], y=i;
                if(fa[x]==fa[y]) ed[fa[x]].pb(mk(x,y));
                else{
                    if(fa[fa[x]]==y) ed[fa[x]].pb(mk(x,y));
                    else ed[fa[y]].pb(mk(x,y));
                }
            }
        }
    }
    cnt=0;
    for(int i=1;i<=n;++i) vis[i]=0, head[i]=0;
    for(int i=1;i<=n;++i){
        if(vis[i]) continue;
        // cerr << "DFS:" << i << endl;
        res[i].clear(); Q.push(-i);
        solve(i);
    }
    while(!Q.empty()){
        int t=Q.top(); Q.pop();
        work(-t); 
    }
    // int cur=3, las=-1;
    // while(cur) {
    //     // cerr << cur << endl;
    //     las=cur; cur=fa[cur];
    //     if(cur==0) break;
    //     if(G[cur][0]==las || G[cur].back()==las) cerr << "GOOD" << endl;
    //     else cerr << "BAD" << endl;
    // }
    putchar(10);
}

int main(){
    freopen("graperm.in","r",stdin);
    freopen("graperm.out","w",stdout);
    int c, t;
    c=read(), t=read();
    while(t--) solve();
    return 0;
}
/*
0 1
10 11
3 6
4 9
1 7
9 6
6 7
1 9
2 3
7 8
2 8
2 6
7 10
*/