我的博客鏈接:https://startcraft.cn
題目鏈接:https://codeforces.com/contest/1136/problem/D
題目大意:
給你1-n的n個數,給出一個序列,序列由這n個數組成,然後給m對數u,v,一對數u,v代表在序列中如果u和v相鄰,且u在v的左邊,則可以交換這兩個數,現在問你序列中的最後一個數最多可以向左移動幾次
思路:設序列中最後一個數爲x,考慮哪些數在x的左邊時可以將x向左移動,將這些數用一個集合V記錄下來。
用一個指針pos指向最後一個數,然後看pos左邊的數是不是這些記錄下來的數中的一個,如果是的話就交換這兩個數,並且將左邊這個數從集合V中刪除,然後pos-1,ans++,如果不是,則看pos左邊這個數y,看可以將y向左移動的數有哪些,將這些數與V求交集然後更新V,因爲X只能與V中的數交換,所以能與y交換的數若不在V中就不能使得X左移,然後讓pos指向y,重複上面的過程,直到V爲空集或者指着走到最左邊
AC代碼:
#include <iostream>
#include <set>
#include <queue>
#include <cstdio>
using namespace std;
typedef long long ll;
#define wfor(i,j,k) for(i=j;i<k;++i)
#define mfor(i,j,k) for(i=j;i>=k;--i)
// void read(int &x) {
// char ch = getchar(); x = 0;
// for (; ch < '0' || ch > '9'; ch = getchar());
// for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
// }
const int maxn = 3e5 + 5;
int num[maxn];
int vis[maxn];
set<pair<int, int>>st;
int main()
{
std::ios::sync_with_stdio(false);
#ifdef test
freopen("F:\\Desktop\\question\\in.txt", "r", stdin);
#endif
#ifdef ubuntu
freopen("/home/time/debug/debug/in", "r", stdin);
freopen("/home/time/debug/debug/out", "w", stdout);
#endif
int n, m;
cin >> n >> m;
int i;
wfor(i, 0, n)
{
cin >> num[i];
}
queue<int>can;
wfor(i, 0, m)
{
int u, v;
cin >> u >> v;
if (v == num[n - 1])
{
can.push(u);//記錄可以使得num[n-1]向左移動的數
vis[u] = 1;
}
st.insert(make_pair(u, v));
}
int pos = n - 1;
int ans = 0;
while (pos > 0 && !can.empty())
{
if (vis[num[pos - 1]] == 1)//可以向左移動
{
ans++;
vis[num[pos - 1]] = 0;//從集合中刪除這個數
swap(num[pos - 1], num[pos]);
} else//不可以移動,求交集,更新集合
{
int len = can.size();
wfor(i, 0, len)
{
int t = can.front();
can.pop();
if (vis[t])//這個數未被集合剔除
{
if (st.count(make_pair(t, num[pos - 1])) != 0)//看num[pos-1]可否通過t換到左邊去(求交集的過程)
{
can.push(t);
} else
vis[t] = 0;
}
}
}
pos--;
}
cout << ans << endl;
return 0;
}