要求在一个DAG中删去不多于k条边,使得拓扑序的字典序最大。
贪心策略:每次删去入度小于res的,序号尽量大的点的入边。
需要用线段树维护区间最小值。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<vector> using namespace std; const int N = 400000 +10; const int inf = 1e8+10; vector<int> G[N]; int d[N]; int tops[N],minv[N]; int n,m,k,u,v,res,num; int query(int o,int L,int R) { int M = L + (R-L)/2; if(L==R) return L; if(minv[o*2+1]<=res) return query(o*2+1,M+1,R); return query(o*2,L,M); } int p,fl[N]; void update(int o,int L ,int R) { int M = L + (R-L)/2; if(L==R){ minv[o] = v; } else{ if(p <= M) update(o*2,L,M); else update(o*2+1,M+1,R); minv[o] = min(minv[o*2],minv[o*2+1]); } } void change(int pl , int x) { p =pl; v = x; update(1,1,n); } void build() { for(int i=1;i<=n;i++){ p = i; v = d[i]; update(1,1,n); } } void solve() { for(int i=1;i<=n;i++){ int id = query(1,1,n); int tmp; res -= d[id]; fl[id]=1; tops[++num] = id; change(id,inf); int size = G[id].size(); for(int j=0;j<size;j++) { tmp = G[id][j]; if(fl[tmp]) continue; d[tmp]--; change(tmp,d[tmp]); } } } int main(){ while(scanf("%d%d%d",&n,&m,&k)!=EOF){ res = k; num = 0; memset(d,0,sizeof(d)); memset(fl,0,sizeof(fl)); for(int i=1;i<=n;i++) G[i].clear(); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); G[u].push_back(v); d[v]++; } build(); solve(); for(int i=1;i<=num;i++){ printf("%d",tops[i]); if(i==num) printf("\n"); else printf(" "); } } return 0; }
时间: 2024-11-05 13:44:33