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;
}
posted @ 2020-10-16 18:42  jz_597  阅读(111)  评论(0编辑  收藏  举报