CF1682E 构造+拓扑排序
link
跟其他几篇题解不太一样。
思路
\text{Step 1}
对于一个排列
结论:假如图上共有
为什么呢?
设图上环的数量为
- 交换同一个简单环内两数,会把这个环断开重连为两个小环,
\phi 增加1 (当\phi<n 时一定能找到简单环)。 - 交换不同环的两数,会把这两个环打通成一个大环,
\phi 减少1 。
最优方案显然是一直让
\text{Step 2}
考虑将这
假设存在两条边
- 若
A 和B 不在同一环内,它们不会互相影响。 - 若
A 和B 完全无交(在边上和顶点上都无交),它们不会互相影响。 - 若
A 和B 在边上相交,无论先做A 再做B 还是先做B 再做A ,都会导致后做的跨环,一定无解,所以不存在这种情况,也不用讨论。
还剩下一种情况:
读者可以自己手玩一下题目中的两个样例(其实是我太懒了不想画图),然后就会发现:对于这种情况,需要顺着环旋转的方向做交换,才能保证不跨环。
于是,再建一个以
复杂度
代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+7;
int to[N],rk[N],in[N],len[N],cir_cnt,deg[N],n,m,q[N];
vector<pii> G[N];//加入的无向交换边
vector<int> g[N];//新图
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int i,j,k,x,y;
memset(rk,-1,sizeof rk);
cin>>n>>m;
for(i=1;i<=n;++i) cin>>to[i];//to存储原有向图
for(i=1;i<=m;++i) cin>>x>>y,G[x].emplace_back(y,i),G[y].emplace_back(x,i);
//找环
for(i=1;i<=n;++i)if(rk[i]==-1)
{
rk[i]=0,in[i]=++cir_cnt;//rk-环上的排名
for(j=to[i],k=1;j!=i;j=to[j],++k) rk[j]=k,in[j]=cir_cnt;
len[cir_cnt]=k;//环长
}
//排序 建新图
for(i=1;i<=n;++i)
{
int L=len[in[i]],ri=rk[i];
sort(G[i].begin(),G[i].end(),[&](pii x,pii y)
{
int rx=rk[x.first]<=ri?rk[x.first]+L:rk[x.first];
int ry=rk[y.first]<=ri?rk[y.first]+L:rk[y.first];
return rx<ry;
});
for(j=1;j<(int)G[i].size();++j)
g[G[i][j-1].second].push_back(G[i][j].second),++deg[G[i][j].second];
}
//拓扑排序
int hd=1,tl=0,u;
for(i=1;i<=m;++i)if(deg[i]==0)q[++tl]=i;
while(hd<=tl)
{
u=q[hd++],cout<<u<<' ';
for(int v:g[u])if(!--deg[v])q[++tl]=v;
}
return 0;
}