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