IOI2021集训队作业 181SG Graph
一个DAG,你可以加至多\(k\)条边,使它的字典序最小的拓扑序最大。
\(n\le 10^5\)
维护两个集合\(S\)和\(T\)分别表示零度点和加了某条入边的点。
贪心地钦定即可。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 100010
#define K 100010
int n,m,k;
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int d[N];
set<int> S,T;
int ans[N],pre[N];
void modify(int x){
for (EDGE *ei=last[x];ei;ei=ei->las)
if (!--d[ei->to])
S.insert(ei->to);
}
int main(){
freopen("in.txt","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
e[ne]={v,last[u]};
last[u]=e+ne++;
d[v]++;
}
for (int i=1;i<=n;++i)
if (d[i]==0)
S.insert(i);
for (int i=1;i<=n;++i){
if (S.empty()){
auto p=--T.end();
ans[i]=*p;
pre[*p]=ans[i-1];
modify(*p);
T.erase(p);
}
else{
while (k && S.size()>1){
auto p=S.begin();
T.insert(*p);
S.erase(*p);
--k;
}
auto p=S.begin();
if (!T.empty() && k && *--T.end()>*p){
T.insert(*p);
S.erase(p);
--k;
p=--T.end();
ans[i]=*p;
pre[*p]=ans[i-1];
modify(*p);
T.erase(p);
}
else{
ans[i]=*p;
modify(*p);
S.erase(p);
}
}
}
for (int i=1;i<=n;++i)
printf("%d ",ans[i]);
printf("\n");
int cnt=0;
for (int i=1;i<=n;++i)
if (pre[i])
++cnt;
printf("%d\n",cnt);
for (int i=1;i<=n;++i)
if (pre[i])
printf("%d %d\n",pre[i],i);
return 0;
}