题目链接:https://codeforces.com/problemset/problem/1136/D
题意:
给出 $1 sim n$ 的某个排列 $p$,再给出若干 $(x,y)$ 表示当序列中出现 $x,y$ 时,两者可以交换位置。问序列中最末尾的数可以前进多少步。
题解:
如果 $p[n-1]$ 可以与 $p[n]$ 交换位置,那么肯定是立刻交换,因为首先 $p[n-1]$ 只能最多只能产生 $1$ 步的贡献,同时就算把 $p[n-1]$ 往前换,等到在未来某个时刻再跟 $p[n]$ 交换,也不可能使得前进步数更多(自己画画就能明白),因此不如立刻换掉。
如果此时 $p[n-1]$ 与 $p[n]$ 不能交换位置,那么想要前进,必然要找一个能 $p[x]$ 能和我 $p[n]$ 交换的,和上面同样的道理,找远的 $x$ 不会比找近的 $x$ 更优,因此优先找最近的那个 $p[x]$ 来跟我交换位置即可。
(这题的序列存储我用了数组模拟链表,时间复杂度是 $O(n log m)$,不知道我是不是写复杂了……)
AC代码:
#include<bits/stdc++.h> #define pb(x) push_back(x) using namespace std; const int maxn=3e5+10; const int maxm=5e5+10; int n,m; int a[maxn]; int head,tail,pre[maxn],nxt[maxn]; map<int,bool> mp[maxn]; bool check(int x,const vector<int>& v) { for(auto y:v) if(mp[x][y]==0) return 0; return 1; } int main() { ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); cin>>n>>m; nxt[head=0]=1, pre[tail=n+1]=n; for(int i=1;i<=n;i++) cin>>a[i], pre[i]=i-1, nxt[i]=i+1; for(int i=1,x,y;i<=m;i++) cin>>x>>y, mp[x][y]=1; vector<int> need; need.pb(a[n]); for(int x=pre[n];x>head;x=pre[x]) { if(check(a[x],need)) { int L=pre[x], R=nxt[x]; nxt[L]=R, pre[R]=L; } else need.pb(a[x]); } int cnt=0; for(int p=nxt[head];p<tail;p=nxt[p]) cnt++; cout<<n-cnt<<endl; }