「Luogu P5603」小O与桌游

题目链接

戳我

\(Solution\)

我们来分析题目。

实际上就是求一个拓扑序满足拓扑序的前缀最大值最多/最少

对于第一种情况,很明显一直选当前能选的最小的是最优的对吧。因为你需要大的尽可能多。用个堆维护就好了

但是很多人第二种情况想当然了,认为一直取最大值就可以了,但是这种行为太\(Naive\)了。我们需要讨论一下,如果当前能取的最小值不是前缀最大值,则需要先选他,否则就选能选的最大值,因为你选了这个最小值以后可能释放一个比当前最大值更大的值,这样子答案就可能会更优。那么这个怎么维护呢,用个\(set\)就好了,如果不会看下代码吧

\(Code\)

#include<bits/stdc++.h>
#define rg register
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
    return f*x;
}
struct node {
	int to,next;
}a[1000001];
int head[1000001],cnt,n,m,x,y;
int vis[1000001];
int bj[1000001];
set<int> s;
priority_queue<int,vector<int>,greater<int> > p;
void add(int x,int y){
	a[++cnt].to=y;
	a[cnt].next=head[x];
	head[x]=cnt;
}
void solve1(){
	int maxx=0,ans=0;
	while(!p.empty()){
		int now=p.top();
		p.pop();
		if(now>maxx) maxx=now,ans++;
		for(int i=head[now];i;i=a[i].next){
			int v=a[i].to;
			vis[v]--;
			if(!vis[v]) p.push(v);
		}
	}
	printf("%d\n",ans);
}
void solve2(){
	int maxx=0,ans=0;
	while(!s.empty()){
		set<int>::iterator it=s.end();it--;
		set<int>::iterator it2=s.begin();
		int now1=*it2,now=now1;
		if(now1>maxx) now=*it;
		s.erase(now);
		if(now>maxx) maxx=now,ans++;
		for(int i=head[now];i;i=a[i].next){
			int v=a[i].to;
			bj[v]--;
			if(!bj[v]) s.insert(v);
		}
	}
	cout<<ans;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++)
		x=read(),y=read(),add(x,y),vis[y]++,bj[y]++;
	for(int i=1;i<=n;i++)
		if(!vis[i]) s.insert(i),p.push(i);
	solve1();
	solve2();
    return 0;
}
posted @ 2019-10-27 22:32  撤云  阅读(226)  评论(0编辑  收藏  举报
……