Noip模擬7 2021.6.11
前言
考試時候der展了,T1kmp沒特判(看來以後還是能hash就hash),T2搜尋細節沒注意,ans沒清零,130飛到14。。。。
T1 匹配(hash/kmp)
這太水了,其實用個hash隨便打打就過了,不知道當時想什麼非要用個kmp看看那自己的毛片水平到不到位,kmp沒寫假,特判出問題了。。。。
Note:B加一個字元可能比A短,kmp掃描不到,所以要特判一下:
如果加的字元剛好與A的對應那一位相同,那麼整個B就都是A的字首,那麼其x就是lengthB
1 #include<bits/stdc++.h> 2 using namespace std; 3 intView CodeT,nt[300005],la,lb,ans; 4 char ch[3],A[300005],B[300005]; 5 inline void kmp(int len){ 6 nt[1]=0; 7 for(int i=2,j=0;i<=len;i++){ 8 while(j&&B[i]!=A[j+1]) j=nt[j]; 9 if(B[i]==A[j+1]) nt[i]=++j; 10 else nt[i]=0; 11 } 12 } 13 namespace WSN{ 14 inline int main(){15 cin>>T; 16 while(T--){ 17 memset(nt,0,sizeof(nt)); 18 memset(B,0,sizeof(B)); 19 cin>>la>>lb; 20 scanf("%s%s",A+1,ch+1); 21 for(int i=1;i<=lb;i++) B[i]=A[i]; 22 B[++lb]=ch[1]; 23 if(ch[1]==A[lb]){ 24 printf("%d\n",lb); 25 continue; 26 } 27 kmp(lb); 28 printf("%d\n",nt[lb]); 29 } 30 return 0; 31 } 32 } 33 signed main(){return WSN::main();}
T2 回家
這題其實你看看就是個tarjan求割點的板子,然而苣蒻的小馬只記得有向圖縮點tarjan,割點的得用個什麼兒子與父親,剩下的全忘了,於是衝了一發信隊,於是。。。
記得
陣列和變數都清一次0,保證不會掛(不過時間說不定,但總比WA0強)
30分程式碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 inline int read(){ 4 int x=0,f=1; char ch=getchar(); 5 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 6 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 7 return x*f; 8 } 9 int T,n,m,cnt,ind[800005],save[800005],ans,an[800005]; 10 bool vis[800005]; 11 struct SNOW{int from,to,next;}; SNOW e[400005<<1]; int r[400005<<1],tot; 12 inline void add(int x,int y){ 13 if(x==y) return; 14 for(int i=r[x];i;i=e[i].next) if(e[i].to==y) return; 15 e[++tot]=(SNOW){x,y,r[x]}; r[x]=tot; 16 } 17 bool f; 18 inline bool dfs(int x){ 19 // cout<<'x'<<x<<endl; 20 if(x==1) {f=1;return 1;} 21 vis[x]=1; 22 for(int i=r[x];i;i=e[i].next){ 23 if(!vis[e[i].to]&&ind[e[i].to]!=0){ 24 dfs(e[i].to); 25 } 26 } 27 return f==1? 1:0; 28 } 29 inline int check(int x){ 30 for(int i=r[x];i;i=e[i].next) 31 ind[e[i].to]--; 32 ind[x]=0; 33 // cout<<x<<endl; 34 // for(int i=1;i<=n;i++) cout<<ind[i]<<" "; cout<<endl; 35 memset(vis,0,sizeof(vis)); f=0; 36 vis[x]=1; 37 if(dfs(n)) return 0; 38 else return 1; 39 } 40 namespace WSN{ 41 inline int main(){ 42 T=read(); 43 while(T--){ 44 memset(r,0,sizeof(r)); tot=0; 45 memset(an,0,sizeof(an)); ans=0; 46 memset(ind,0,sizeof(ind)); 47 memset(save,0,sizeof(save)); 48 n=read(),m=read(); 49 for(int i=1;i<=m;i++){ 50 int u=read(),v=read(); 51 add(u,v); add(v,u); 52 ind[u]++; ind[v]++; 53 } 54 for(int i=1;i<=n;i++) save[i]=ind[i]; 55 // for(int i=2;i<n;i++) if(ind[i]==2) cnt++; 56 // if(cnt==n-2&&ind[1]==1&&ind[n]==1){ 57 // printf("%d\n",n-2); 58 // for(int i=2;i<n;i++) printf("%d ",i); putchar('\n'); 59 // continue; 60 // } 61 // cout<<check(4)<<endl; continue; 62 for(int i=2;i<n;i++){ 63 if(check(i)){ ans++; an[ans]=i; } 64 for(int i=1;i<=n;i++) ind[i]=save[i]; 65 } 66 printf("%d\n",ans); 67 if(!ans){ printf("\n");continue; } 68 else{ 69 for(int i=1;i<=ans;i++) 70 printf("%d ",an[i]); 71 putchar('\n'); 72 } 73 } 74 return 0; 75 } 76 } 77 signed main(){return WSN::main();}View Code
正解是在tarjan求割點上加一兩步判斷條件,有一種特別的情況,就是:
1
6 6
1 2
2 3
3 4
2 4
3 5
4 6
這組樣例畫出來就明白了,他1->6必經過3,4;而割點有2,3,4,所以只求割點是不行的
那麼加一個vis陣列用來記錄跑深搜的時候是否經歷過這個點,經歷過並且符合割點條件的就是答案.
1 #include<bits/stdc++.h> 2 using namespace std; 3 inline int read(){ 4 int x=0,f=1; char ch=getchar(); 5 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 6 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 7 return x*f; 8 } 9 int T,n,m,dfn[400005],low[400005],num,root,ans; 10 bool vis[400005],cut[400005]; 11 struct SNOW{int to,next;}; SNOW e[800005]; int r[800005],tot; 12 inline void add(int x,int y){ 13 if(x==y) return; 14 for(int i=r[x];i;i=e[i].next) if(e[i].to==y) return; 15 e[++tot]=(SNOW){y,r[x]}; r[x]=tot; 16 } 17 inline void tarjan(int x){ 18 dfn[x]=low[x]=++num; int flag=0; 19 for(int i=r[x];i;i=e[i].next){ 20 int y=e[i].to; 21 if(!dfn[y]){ 22 tarjan(y);low[x]=min(low[x],low[y]); 23 if(vis[y]) vis[x]=true; 24 if(low[y]>=dfn[x]){ 25 flag++; 26 if((x!=root||flag>1)&&vis[y]&&x!=n&&x!=1) 27 cut[x]=true,ans++; 28 } 29 }else low[x]=min(low[x],dfn[y]); 30 } 31 } 32 namespace WSN{ 33 inline int main(){ 34 T=read(); 35 while(T--){ 36 memset(r,0,sizeof(r)); 37 memset(dfn,0,sizeof(dfn)); 38 memset(low,0,sizeof(low)); 39 memset(vis,0,sizeof(vis)); 40 memset(cut,0,sizeof(cut)); 41 tot=0; num=0; root=1; ans=0; 42 n=read(); m=read(); 43 for(int i=1;i<=m;i++){ 44 int u=read(),v=read(); 45 add(u,v); add(v,u); 46 } 47 vis[n]=1; tarjan(1); 48 printf("%d\n",ans); 49 if(!ans){printf("\n"); continue;} 50 for(int i=2;i<n;i++) if(cut[i]) printf("%d ",i); 51 putchar('\n'); 52 } 53 return 0; 54 } 55 } 56 signed main(){return WSN::main();}View Code
T3 壽司(sushi)
思路一:
戰神提供的nlogn做法。以每個R為斷點記錄字首和B和字尾和B兩個陣列,可推出一個柿子:
ans=Σmin(x,y)=Σ(li+ri+∣li−ri∣/2) ans= \Sigma min(x,y) =\Sigma({l_i+r_i+\mid l_i-r_i \mid}/2)ans=Σmin(x,y)=Σ(li+ri+∣li−ri∣/2)
然後將前面的l+r看成整體算出,後面的找規律:
破環成鏈時將字串複製一倍,以原字串長度向右每次移一位。
如果加入一個B,則l-r的值每個都對應-2;
如果加入一個R,則第一組差值取相反數並跑到最後,其餘不變並上移一位。
例如:
/*12
BBRBBRBBBRRR
BRBBRBBBRRRB
RBBRBBBRRRBB
BBRBBBRRRBBR
BRBBBRRRBBRB
RBBBRRRBBRBB
BBBRRRBBRBBR
BBRRRBBRBBRB
BRRRBBRBBRBB
RRRBBRBBRBBB
RRBBRBBRBBBR
RBBRBBRBBBRR
2 5|4 3|7 0|7 0|7 0|
1 6|3 4|6 1|6 1|6 1|
0 7|2 5|5 2|5 2|5 2|
2 5|5 2|5 2|5 2|7 0|
1 6|4 3|4 3|4 3|6 1|
0 7|3 4|3 4|3 4|5 2|
3 4|3 4|3 4|5 2|7 0|
2 5|2 5|2 5|4 3|6 1|
1 6|1 6|1 6|3 4|5 2|
0 7|0 7|0 7|2 5|4 3|
0 7|0 7|2 5|4 3|7 0|
0 7|2 5|4 3|7 0|7 0|
一組|中間的的每兩個為斷點左右絕對值*/
1 #include<bits/stdc++.h> 2 using namespace std; 3 inline int read(){ 4 int x=0,f=1; char ch=getchar(); 5 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 6 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 7 return x*f; 8 } 9 int T,numB,ans,tot,frt[1000005],bak[1000005]; 10 char s[1000005]; 11 namespace WSN{ 12 inline int main(){ 13 T=read(); 14 while(T--){ 15 memset(frt,0,sizeof(frt)); tot=1; 16 memset(bak,0,sizeof(bak)); ans=0; 17 numB=0; 18 scanf("%s",s+1); 19 int len=strlen(s+1); 20 for(int i=1;i<=len;i++){ 21 if(s[i]=='B') 22 numB++,frt[tot]++; 23 if(s[i]=='R') tot++; 24 } 25 for(int i=1;i<=tot;i++) 26 frt[i]+=frt[i-1], bak[i]=numB-frt[i]; 27 for(int i=1;i<tot;i++) cout<<frt[i]<<" "<<bak[i]<<"|"; 28 putchar('\n'); 29 } 30 return 0; 31 } 32 } 33 signed main(){return WSN::main();}View Code
這樣的話還要用大根堆維護,求出每組數差值的變化量以便統計最大的減數(也就是絕對值那一坨)
就在我還在苦思如何使用大根堆時,正解的n複雜度出現了
B哥大定理(BBT)
通過巨量的打表發現規律:只求複製後卡的區間的每個中點求出R向兩側移動的步數的最小值便是最優值。
這樣通過維護每一個點的字首B個數,字尾B個數,字首R個數,R向左移的步數及向右移的步數,就可O(1) O(1)O(1)求出步數
再每中情況去中點掃一遍就行:
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 inline int read(){ 5 int x=0,f=1; char ch=getchar(); 6 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 7 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 8 return x*f; 9 } 10 const int NN=2000005; 11 int T,ans,tot,lb[NN],rb[NN],lr[NN],stl[NN],str[NN],numB; 12 char s[NN]; 13 inline int qiu(int l,int mid,int r){ 14 return stl[mid]-stl[l-1]-lb[l-1]*(lr[mid]-lr[l-1])+str[mid+1]-str[r+1]-rb[r+1]*(lr[r]-lr[mid]); 15 } 16 namespace WSN{ 17 inline int main(){ 18 T=read(); 19 while(T--){ 20 ans=0x3ffffffffffff; 21 scanf("%s",s+1); 22 int len=strlen(s+1); 23 for(int i=1;i<=len;i++) s[i+len]=s[i]; 24 len*=2; 25 for(int i=1;i<=len;i++){ 26 lb[i]=lb[i-1]; 27 lr[i]=lr[i-1]; 28 stl[i]=stl[i-1]; 29 if(s[i]=='B') lb[i]++; 30 else lr[i]++, stl[i]+=lb[i]; 31 } 32 for(int i=len;i>=1;i--){ 33 rb[i]=rb[i+1]; 34 str[i]=str[i+1]; 35 if(s[i]=='B') rb[i]++; 36 else str[i]+=rb[i]; 37 } 38 int n=len/2; 39 for(int i=1;i<=n;i++){ 40 int mid=(i+i+n-1)>>1; 41 ans=min(ans,qiu(i,mid,i+n)); 42 } 43 printf("%lld\n",ans); 44 } 45 return 0; 46 } 47 } 48 signed main(){return WSN::main();}View Code
END
這次考試總覺得應拿到130或稍微第一點,可是呢,失誤太多了,以後看到題能用必對的方法就不用拿不準的(總之hash比對)