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);
}
}
}