「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;
}