9.6 上午 becoder 模擬賽總結&題解

tkdqmx發表於2024-09-06

T1 語言

水題不多說,很容易發現 NP 需要滿足的只是最後一個單詞為 N,前面是 A 或 N 都可以隨意放。

所以用兩個陣列,\(v1_i\) 記錄以 \(i\) 結尾的字首是否可以構成 NP,\(v2_i\) 記錄以 \(i\) 為開頭的字尾是否可以構成 NP。

最後 for 迴圈掃一遍是否有同時滿足 \(v1_{i-1}=true\)\(v2_{i+1}=true\) 的就可以了。

時間複雜度 \(O(n)\),程式碼如下(100pts):

#include<bits/stdc++.h>
using namespace std;
char ch[100005];
int t,len,flag,w[26],v1[100005],v2[100005];
int main(){
    scanf("%d",&t);
    while(t--){
        for(int i=0;i<26;i++)  scanf("%d",w+i);
        memset(v1,0,sizeof v1);
        memset(v2,0,sizeof v2),flag=0;
        scanf("%s",ch+1),len=strlen(ch+1);
        for(int i=1;i<=len;i++){
            if(!(w[ch[i]-'a']&1)&&!(w[ch[i]-'a']&2))  break;
            if(w[ch[i]-'a']&2)  v1[i]=1;
        }
        if(w[ch[len]-'a']&2){
            for(int i=len;i>0;i--){
                if(!(w[ch[i]-'a']&1)&&!(w[ch[i]-'a']&2))  break;
                v2[i]=1;
            }
        }
        for(int i=2;i<len;i++)  flag|=(v1[i-1]&&v2[i+1]&&(w[ch[i]-'a']&4));
        printf("%s\n",flag?"Yes":"No");
    }
}

T2 色球

很明顯的雙向連結串列的題目,但這道題一定不能去想哪邊是 \(pre\) 哪邊是 \(next\),不然就會把自己給繞進去。

對於連結串列的每一塊,維護 \(col\)\(val\) 兩個資訊,而對於每個桶,維護裡面連結串列的表頭和表尾就行了。

對於操作 1,我們把當前找的這個連結串列的表頭,那邊沒有值就把那邊賦值成現在加進去的這一塊。

對於操作 2,如果現在的這個表頭的 \(val\) 已經大於等於 \(x\) 了,就直接 break,然後輸出表頭的 \(col\) 就行了。

否則,因為表頭一定只有一邊有值,我們直接往那一邊跳就行了。

而對於現在跳到的這一個點,哪邊是剛才的表頭,就直接賦值成 \(0\) 就行了,另外一邊就是它下面的數。

對於操作 3,直接把兩個表頭連起來,然後把 \(v\) 的表頭賦值成 \(u\) 的表尾就行了。

程式碼如下(100pts):

#include<bits/stdc++.h>
using namespace std;
#define N 200005
char op[10];
int n,m,cnt,hed[N],tal[N],col[N],val[N],ch1[N],ch2[N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,z;i<=m;i++){
        scanf("%s%d%d",op,&x,&y);
        if(op[2]=='s'){
            scanf("%d",&z),cnt++;
            if(!tal[z])  tal[z]=cnt;
            col[cnt]=y,val[cnt]=x,ch1[cnt]=hed[z];
            (ch1[hed[z]]?ch2[hed[z]]=cnt:ch1[hed[z]]=cnt),hed[z]=cnt;
        }
        if(op[2]=='p'){
            while(val[hed[y]]<x){
                x-=val[hed[y]];
                int to=(ch1[hed[y]]?ch1[hed[y]]:ch2[hed[y]]);
                (ch1[to]==hed[y]?ch1[to]=0:ch2[to]=0),hed[y]=to;
            }
            val[hed[y]]-=x,printf("%d\n",col[hed[y]]);
        }
        if(op[2]=='t'){
            if(!hed[x])  continue;
            if(!hed[y])  hed[y]=tal[x],tal[y]=hed[x];
            else{
                ch1[hed[y]]?ch2[hed[y]]=hed[x]:ch1[hed[y]]=hed[x];
                ch1[hed[x]]?ch2[hed[x]]=hed[y]:ch1[hed[x]]=hed[y];
                hed[y]=tal[x];
            }
            tal[x]=hed[x]=0;
        }
    }
}

T3 斐波

完全不會,寫的 10pts 暴力...

T4 偶數

先寫一下 40pts 的做法吧,100pts 的等我過了再來寫。

首先,我們把 \(u\) 設成 \(vv\) 的形式,那麼擴充套件後就一定是 \(vwvw\) 的形式.

其中,\(w\)\(v\) 的一個字首,且是 \(v\) 的週期,這個性質可以手模得出。

而顯然的一點,\(w\) 肯定為 \(v\) 的週期中最短的字首。

那我們就可以透過對 \(v\) 做 kmp 來求出 \(w\),再把 \(vw\) 作為新的 \(v\)\(w\),知道 \(u\) 的長度大於 \(n\)

最後的詢問就可以透過類似字串 Hash 求解區間 Hash 值的方法,用字首和來解決了。

時間複雜度 \(O(n+q)\),程式碼如下(40pts):

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define N 200005
char ch[N];
const LL mod=998244353;
LL t,n,q,po[N],sum[N],Next[N];
int main(){
    scanf("%lld",&t);
    while(t--){
        Next[0]=Next[1]=0,po[0]=1;
        scanf("%s%lld%lld",ch,&n,&q);
        int len=strlen(ch)/2,i=1;
        while(len<n){
            for(;i<len;i++){
                int j=Next[i];
                while(ch[i]!=ch[j]&&j)  j=Next[j];
                Next[i+1]=(j+1)*(ch[i]==ch[j]);
            }
            int tlen=len-Next[len];
            for(int j=0,k=len;j<tlen;j++,k++)  ch[k]=ch[j];
            len+=tlen;
        }
        for(int i=1;i<=n;i++)
            po[i]=po[i-1]*10%mod,sum[i]=(sum[i-1]*10+ch[i-1]-'0')%mod;
        for(int i=1,l,r;i<=q;i++){
            scanf("%d%d",&l,&r);
            printf("%lld\n",(sum[r]-sum[l-1]*po[r-l+1]%mod+mod)%mod);
        }
    }
}