其他分享
首页 > 其他分享> > 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​+∣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比对)

标签:11,ch,Noip,2021.6,int,read,while,ans,getchar
来源: https://www.cnblogs.com/hzoi-wsn/p/14890715.html