Codeforces Round #681 (Div. 1, based on VK Cup 2019-2020 - Final) B. Identify the Operations (模擬,雙向連結串列)
阿新 • • 發佈:2020-12-02
-
題意:給你一組不重複的序列\(a\),每次可以選擇一個數刪除它左邊或右邊的一個數,並將選擇的數append到陣列\(b\)中,現在給你陣列\(b\),問有多少種方案數得到\(b\).
-
題解:我們可以記錄\(b_i\)在\(a_i\)中的位置,然後列舉\(b_i\),取它在\(a_i\)的位置,然後看\(a_{i-1}\)和\(a_{i+1}\)的情況,因為我們append之後必須要刪除\(a_{i-1}\)和\(a_{i+1}\)中的一個,並且所有元素都是不重複的,所以\(a_{i-1}\)和\(a_{i+1}\)必然不能出現在\(b_{i+1}...b_{n}\)中,而當我們append \(a_i\)
所以我們可以討論\(a_{i-1}\)和\(a_{i+1}\)的情況,假如它們兩個都在\(b_{i+1}...b_{n}\)中出現,那麼我們肯定不能構造出\(b\),直接\(ans=0\)然後結束,假如它們兩個中有一個在\(b_{i+1}...b_n\)中出現,那麼我們刪除另外一個,因為刪除的方案是固定的,所以對答案沒有貢獻,假如它們兩個都沒有出現,因為\(a_{i-1},a_i,a_{i+1}\)都是沒有用的數,所以我們可以刪去\(a_{i-1}\)或\(a_{i+1}\)中的任意一個,並且\(ans*=2\).
具體實現我們可以用雙向連結串列,並且標記\(b_i,...,b_n\) -
程式碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 998244353 ; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} struct misaka{ int pre; int nxt; }e[N]; int t; int n,m; int a[N],b[N]; int pos[N]; bool cnt[N]; void init(){ rep(i,1,n){ e[i].pre=i-1; e[i].nxt=i+1; } e[1].pre=0; e[n].nxt=0; } void Delete(int x){ if(e[x].pre) e[e[x].pre].nxt=e[x].nxt; if(e[x].nxt) e[e[x].nxt].pre=e[x].pre; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>t; while(t--){ cin>>n>>m; rep(i,1,n) cnt[i]=false; rep(i,1,n){ cin>>a[i]; pos[a[i]]=i; } rep(i,1,m){ cin>>b[i]; b[i]=pos[b[i]]; //對映到a陣列的位置 cnt[b[i]]=true; } init(); //雙向連結串列的初始化 cnt[0]=true; int ans=1; rep(i,1,m){ if(cnt[e[b[i]].pre]){ if(cnt[e[b[i]].nxt]){ ans=0; break; } else{ Delete(e[b[i]].nxt); } } else{ if(cnt[e[b[i]].nxt]){ Delete(e[b[i]].pre); } else{ ans=ans*2%mod; Delete(e[b[i]].nxt); } } cnt[b[i]]=false; } cout<<ans<<'\n'; } return 0; }