Codeforces 1136D - Nastya Is Buying Lunch - [贪心+链表+map]

题目链接: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;
}

猜你喜欢

转载自www.cnblogs.com/dilthey/p/10555908.html