题目传送门
题目描述:
Solution
看到第一个限制,我们就能想到连边。
但是,我们如果建正图,很显然对于多个关系会难以处理。
因此,我们考虑建反图。
如果一个序列是好的,那么必然满足逆拓扑序。
我们用堆来维护当前的拓扑序。
如果当前点的最晚限制是
k
[
i
]
k[i]
k[i],那么在当前这个点之前一定要有
n
−
k
[
i
]
n-k[i]
n−k[i]个点。
用小根堆维护
n
−
k
[
i
]
n-k[i]
n−k[i],对于第一问我们不断做拓扑序即可。
考虑第二问
将问题转化,对于当前数
x
x
x,它所在的位置下标最小,就是在反图中它出现的位置尽可能靠后。
所以,如果我们在拓扑序中第一次遍历到了
x
x
x,我们不考虑将
x
x
x入队,继续做拓扑序,直到找到第一个不满足条件的点,就是它在反图中能出现的最晚位置。这样便是
x
x
x出现的最早位置。
但是。。显然这样的复杂度不能通过这题
所以我们开个
O
2
O2
O2就好啦!
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+10;
typedef pair < int , int > pii;
struct Node{
int y,Next;
}e[2*N];
int len , linkk[N];
int n,m;
int k[N] , in[N] , In[N];
int ans[N] , cnt;
priority_queue < pii , vector < pii > , greater < pii > > q;
void Insert(int x,int y){
e[++len] = (Node){y,linkk[x]};
linkk[x] = len;
}
void Topsort(){
for (int i = 1; i <= n; i++) in[i] = In[i];
for (int i = 1; i <= n; i++)
if (!in[i]) q.push({n-k[i],i});
while (!q.empty()){
int x = q.top().second; q.pop();
ans[++cnt] = x;
for (int i = linkk[x]; i; i = e[i].Next){
int y = e[i].y;
in[y]--;
if (in[y] == 0) q.push({n-k[y],y});
}
}
}
int Solve(int xx){
while (!q.empty()) q.pop();
for (int i = 1; i <= n; i++) in[i] = In[i];
int Ans = 0;
for (int i = 1; i <= n; i++)
if (!in[i]) q.push({n-k[i],i});
while (!q.empty()){
int x = q.top().second; q.pop();
if (x == xx) continue;
if (n-Ans > k[x]) return n-Ans;
Ans++;
for (int i = linkk[x]; i; i = e[i].Next){
int y = e[i].y;
in[y]--;
if (in[y] == 0) q.push({n-k[y],y});
}
}
return n-Ans;
}
int main(){
scanf("%d %d",&n,&m);
for (int i = 1; i <= n; i++) scanf("%d",&k[i]);
for (int i = 1,x,y; i <= m; i++)
scanf("%d %d",&x,&y) , Insert(y,x) , In[x]++;
Topsort();
for (int i = cnt; i; i--) printf("%d ",ans[i]);
printf("\n");
for (int i = 1; i <= n; i++)
printf("%d ",Solve(i));
}