1. 程式人生 > 其它 >Noip模擬7 2021.6.11

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 int
T,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();}
View Code

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+liri/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比對)