HDU - 5638

题目链接:HDU - 5638


考虑最小字典序,显然可以贪心求。对于每次的点来说,看是否可以删边使得更小的点入队。

然后我们可以发现,对于拓扑排序来说,删边的实质就是使得某个点的入度减1。然后我们可以用线段树维护每个点的入度,然后每次可以取出一个最小的,入度满足小于等于k的点,看是否通过操作使其入队。

维护最小字典序的队列,我们换成优先队列即可。注意细节,不要让一个点重复入队。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f,mod=1e9+7;
int n,deg[N],m,k,res,pos,mi[N<<2],cnt,vis[N];
vector<int> g[N];
#define mid (l+r>>1)
void change(int p,int l,int r,int x,int v){
	if(l==r){mi[p]=v;	return ;}
	if(x<=mid)	change(p<<1,l,mid,x,v);
	else	change(p<<1|1,mid+1,r,x,v);
	mi[p]=min(mi[p<<1],mi[p<<1|1]);
}
int ask(int p,int l,int r,int v){
	if(l==r)	return l;
	if(mi[p<<1]<=v)	return ask(p<<1,l,mid,v);
	else	return ask(p<<1|1,mid+1,r,v);
}
void Top(){
	priority_queue<int,vector<int>,greater<int> > q; cnt=0;
	for(int i=1;i<=n;i++){
		if(!deg[i])	q.push(i),change(1,1,n,i,inf),vis[i]=1;
		else	change(1,1,n,i,deg[i]);
	}
	while(q.size()){
		if(mi[1]>k)	pos=inf;
		else	pos=ask(1,1,n,k);
		if(q.top()>pos)	q.push(pos),k-=deg[pos],change(1,1,n,pos,inf),vis[pos]=1;
		int u=q.top();	q.pop(); res=(res+1LL*(++cnt)*u%mod)%mod;
		for(int to:g[u]){
			if(--deg[to]==0&&!vis[to])	change(1,1,n,to,inf),q.push(to),vis[to]=1;
			if(deg[to]>0&&!vis[to])	change(1,1,n,to,deg[to]);
		}
	}
	printf("%d\n",res);
}
void solve(){
	scanf("%d %d %d",&n,&m,&k); res=0;
	for(int i=1;i<=n;i++)	g[i].clear(),deg[i]=vis[i]=0;
	for(int i=1,a,b;i<=m;i++) scanf("%d %d",&a,&b),g[a].push_back(b),deg[b]++;
	Top();
}
signed main(){
	int T; cin>>T; while(T--) solve();
	return 0;
}

你可能感兴趣的:(拓扑排序,HDU,贪心)