【题解】CF1477D Nezzar and Hidden Permutations
神仙图论题。
给定一张无向图,对于每个点指定两个排名 \(p_i,q_i\),使得对于每条边 \((u,v)\),\((p_u-p_v)(q_u-q_v)>0\) ,也就是相对排名相同。构造两个排列 \(p,q\) 使得 \(p_i\neq q_i\) 的位置最多。
转换一下,对于每条边指定一个方向,\(p,q\) 就是这张有向图的拓扑序。
我们知道拓扑序的性质,度数为 \(n-1\) 的点在拓扑序中的位置是固定的,因为每个点必定在它前面或后面,所以我们可以一直删除这样的点直到图中不存在度数为 \(n-1\) 的点。
删点我们可以用优先队列动态维护未删除的点中度数最大的点。
新图看起来没有什么区别,但是对所有边取反后发现每个点的度数至少为 \(1\) 。
在反图中,如果只有两个点,任意排列都是满足条件的。
如果有三个点,则有一个中点 \(x\)。手算一下只有 \(p_x=1,q_x=3\) ,其余两点相对顺序相同时是满足条件。
在反图中删边,相当于在原图中加边,所以反图删边后构造的方案也一定成立。
现在考虑四个点的情况,大部分情况可以通过删除一条边得到两个只有两个点的情况,可以递归处理。
但是只有菊花图不能递归,但是类似于三个点的方案,我们令中心为 \(x\), 构造 \(p_x=1,q_x=4\) ,其余三点相对顺序相同即可。
这启发我们将反图割去一些边后形成若干歌互不相干的菊花图来做。
考虑依次加入每个点,假设当前加入的点为 \(u\) 。
如果与 \(u\) 相邻的点都不处于某个菊花中,\(u\) 与其相邻的点构成一个菊花图。
令处于某个菊花中且与 \(u\) 相邻的点为 \(v\) ,如果 \(v\) 所处的菊花大小为 \(2\) ,则加入 \(u\) 后没有影响。
否则可以将 \(v\) 与原来的中心断开,与 \(v\) 形成一个以 \(u\) 为中心的新的菊花图。
反图的边很多,考虑数据结构优化。这里的做法是开两个 set
分别维护还没有加入的节点和已经处于某个菊花图中的点。
时空复杂度 \(\mathcal{O}((n+m)\log (n+m))\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
using namespace std;
int n,m,h[N],tot,in[N],v[N],a[N],b[N],idx,u[N];
struct edge{int to,nxt;}e[N<<1];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
typedef pair<int,int> Pr;
priority_queue<Pr>q;set<int>s,t;
int mat[N],col,cen[N],sz[N];
vector<int>c[N];
void solve(){
puts("No Copy");scanf("%d%d",&n,&m);
while(!q.empty())q.pop();
rep(i,1,n)h[i]=v[i]=in[i]=mat[i]=sz[i]=0,c[i].clear();
tot=col=0;s.clear();t.clear();
rep(i,1,m){
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);in[x]++;in[y]++;
}
idx=0;int w=n;rep(i,1,n)q.push(make_pair(in[i],i));
while(!q.empty()){
int now=q.top().second;
if(in[now]!=w-1)break;
q.pop();a[now]=b[now]=++idx;v[now]=1;w--;
for(int i=h[now];i;i=e[i].nxt)if(!v[e[i].to])in[e[i].to]--,q.push(make_pair(in[e[i].to],e[i].to));
while(!q.empty()){
Pr cur=q.top();
if(v[cur.second]||in[cur.second]<cur.first)q.pop();
else break;
}
}
rep(i,1,n)if(!v[i])s.insert(i);
while(s.size()){
int x=*s.begin(),y=0;s.erase(x);
//cout<<"ss "<<s.size()<<" "<<x<<endl;
for(int i=h[x];i;i=e[i].nxt)u[e[i].to]=1;
for(set<int>::iterator it=t.begin();it!=t.end();it++)if(!u[*it]){y=*it;break;}
t.insert(x);
if(!y){
++col;cen[col]=x;mat[x]=col;sz[col]=1;
for(set<int>::iterator it=s.begin();it!=s.end();){
if(!u[*it])sz[col]++,t.insert(*it),mat[*it]=col,it=s.erase(it);
else it++;
}
for(int i=h[x];i;i=e[i].nxt)u[e[i].to]=0;
}
else if(sz[mat[y]]==2){
cen[mat[y]]=y;sz[mat[y]]++;mat[x]=mat[y];
for(int i=h[x];i;i=e[i].nxt)u[e[i].to]=0;
for(int i=h[y];i;i=e[i].nxt)u[e[i].to]=1;
for(set<int>::iterator it=s.begin();it!=s.end();){
if(!u[*it])sz[mat[y]]++,t.insert(*it),mat[*it]=mat[y],it=s.erase(it);
else it++;
}
for(int i=h[y];i;i=e[i].nxt)u[e[i].to]=0;
}
else{
sz[mat[y]]--;++col;cen[col]=x;mat[x]=mat[y]=col;sz[col]=2;
for(set<int>::iterator it=s.begin();it!=s.end();){
if(!u[*it])sz[col]++,t.insert(*it),mat[*it]=col,it=s.erase(it);
else it++;
}
for(int i=h[x];i;i=e[i].nxt)u[e[i].to]=0;
}
t.insert(x);
}
rep(i,1,n)if(!v[i])c[mat[i]].push_back(i);
rep(i,1,col){
a[cen[i]]=idx+1;
b[cen[i]]=idx+sz[i];
for(int j=0;j<(int)c[i].size();j++)if(cen[i]!=c[i][j])b[c[i][j]]=++idx,a[c[i][j]]=idx+1;
idx++;
}
rep(i,1,n)printf("%d ",a[i]);putchar('\n');
rep(i,1,n)printf("%d ",b[i]);putchar('\n');
}
int main(){
int T;scanf("%d",&T);
while(T--)solve();
return 0;
}