1. 程式人生 > 實用技巧 >【CF1443F】Identify the Operations 題解

【CF1443F】Identify the Operations 題解

原題連結

題意簡介

建議去原題看。這題意我表達不清楚。

大概就是給你一個 n 的排列,現在要求你進行 m 次操作。

每次操作,你會在現有的排列中刪去一個數,然後選擇其左邊或右邊的一個與之相鄰的數加入 b 陣列中。

然後將變成兩截的陣列重新連在一起,更新下標。

現在問有多少種操作方案,可以使 b 陣列中恰好按順序排列著它給定的那幾個數字。

只要兩個方案中任一輪刪數的下標不同,就視為不同的方案。

思路分析

這道題顯然是道水題。不明白為什麼扔在 F。或許出題人是用腳編排題目

其實這道題是同場 div1 的 B 題,之前寫的 CF1443 D 則是 div1 的 A 題。

感覺E題更難一點。

由數字不會重複這一點上看,想要加入一個數字,就需要刪去其中一個與之相鄰的數字。

如果某個數字兩端都是需要在其之後加入的數字,那麼顯然無解。

另外,在刪數過後,後面的數字的下標會往前推一格。判斷方案不同的依據就是被刪除的下標的順序。

如果某個數字原本另一邊就有一個需要保留的數字,那麼前面的所有操作都不可能改變這一點。

而如果數字的兩邊都可以刪除,那麼前面的操作依然不可能改變這一點。

每次操作實質上改變的有且只有下標和加入順序都在它之後的數字的操作對應的 ti 。

在保證不會刪掉還沒加入 b 的需要的數字的前提下,記每個需要的數字可以刪去的與之相鄰的數的個數 c 。

不難發現答案應該是: \(\prod_{i=1}^m c_i\)

程式碼庫

說起來這是我第一次在 memset 上吃癟。

裡頭有一組資料 t=1e5 裡頭全是 n=2 m=1

然後我就 TLE 了。

這個故事告訴我們 t 比較大時 memset 不要亂用。

或者試一下 memset(p,0,(n+1)*sizeof(int)) 。

#include <cstdio>
#include <cstring>
typedef long long ll;
#define REG register
#define rep(i,a,b) for(REG int i=a;i<=b;i++)
const ll N=2e5+5,mod=998244353;
int n,m,A[N],B[N],p[N]; ll ans; bool vis[N]; 
int main(){
    REG int t; scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        //memset(p,0,sizeof(p));
        //memset(vis,0,sizeof(vis));
        rep(i,1,n) p[i]=vis[i]=0;
        rep(i,1,n) scanf("%d",A+i),p[A[i]]=i;
        rep(i,1,m) scanf("%d",B+i),vis[B[i]]=1;
        ans=1;
        rep(i,1,m){
            REG int pos=p[B[i]],c=0;
            if(pos-1>=1&&!vis[A[pos-1]]) c++;
            if(pos+1<=n&&!vis[A[pos+1]]) c++;
            ans=ans*c%mod; vis[B[i]]=0;
        }
        printf("%lld\n",ans);
    }
    return 0;
}