A. 匹配
考试代码($WA18$)


#include<algorithm> #include<iostream> #include<cstring> #include<string> #include<cstdio> #define max(x,y) ((x)>(y)?(x):(y)) #define Maxn 100050 #define INF 0x7fffff #define k1 10007 #define k2 109 #define mod1 1000000007 #define mod2 1000000009 #define Reg register using namespace std; int la,lb,t; char B[Maxn]; long long lss1,lss2,has11[Maxn],has12[Maxn],has21[Maxn],has22[Maxn],pow1[Maxn],pow2[Maxn]; int main() { scanf("%d",&t); while(t--) { has11[0]=has12[0]=has21[0]=has22[0]=0; scanf("%d%d",&la,&lb); string S,K; cin>>S; for(Reg int i=1,x;i<=min(lb+1,la);++i) { x=S[i-1]; has11[i]=(has11[i-1]*k1+x-'a'+1)%mod1; has12[i]=(has12[i-1]*k2+x-'a'+1)%mod2; B[i]=x; } cin>>K; for(Reg int i=0;i<3;++i) { if(K[i]>='a'&&K[i]<='z') { B[++lb]=K[i]; break; } } pow1[0]=pow2[0]=1; for(Reg int i=1;i<=lb;++i) { has21[i]=(has21[i-1]*k1+B[i]-'a'+1)%mod1; has22[i]=(has22[i-1]*k2+B[i]-'a'+1)%mod2; pow1[i]=pow1[i-1]*k1%mod1; pow2[i]=pow2[i-1]*k2%mod2; } int ans=0; for(Reg int i=lb;i>=1;--i) { lss1=(has21[lb]-has21[i]*pow1[lb-i]%mod1+mod1)%mod1; lss2=(has22[lb]-has22[i]*pow2[lb-i]%mod2+mod2)%mod2; if(lss1==has11[lb-i]&&lss2==has12[lb-i]) ans=max(ans,lb-i); } printf("%d\n",ans); } return 0; }
$AC$代码


#include<algorithm> #include<iostream> #include<cstring> #include<string> #include<cstdio> #define max(x,y) ((x)>(y)?(x):(y)) #define Maxn 100050 #define INF 0x7fffff #define k1 10007 #define k2 109 #define mod1 1000000007 #define mod2 1000000009 #define Reg register using namespace std; int la,lb,t; char B[Maxn]; long long lss1,lss2,has11[Maxn],has12[Maxn],has21[Maxn],has22[Maxn],pow1[Maxn],pow2[Maxn]; int main() { scanf("%d",&t); while(t--) { has11[0]=has12[0]=has21[0]=has22[0]=0; scanf("%d%d",&la,&lb); string S,K; cin>>S; for(Reg int i=1,x;i<=min(lb+1,la);++i) { x=S[i-1]; has11[i]=(has11[i-1]*k1+x-'a'+1)%mod1; has12[i]=(has12[i-1]*k2+x-'a'+1)%mod2; B[i]=x; } cin>>K; for(Reg int i=0;i<3;++i) { if(K[i]>='a'&&K[i]<='z') { B[++lb]=K[i]; break; } } pow1[0]=pow2[0]=1; for(Reg int i=1;i<=lb;++i) { has21[i]=(has21[i-1]*k1+B[i]-'a'+1)%mod1; has22[i]=(has22[i-1]*k2+B[i]-'a'+1)%mod2; pow1[i]=pow1[i-1]*k1%mod1; pow2[i]=pow2[i-1]*k2%mod2; } int ans=0; for(Reg int i=lb;i>=0;--i) { lss1=(has21[lb]-has21[i]*pow1[lb-i]%mod1+mod1)%mod1; lss2=(has22[lb]-has22[i]*pow2[lb-i]%mod2+mod2)%mod2; if(lss1==has11[lb-i]&&lss2==has12[lb-i]) ans=max(ans,lb-i); } printf("%d\n",ans); } return 0; }
低错总结:
枚举不够。。从$l_b$枚举到了$1$,结果$WA18$,
然后考试后把$1$改成了$0$,然后过了。
B. 回家
考试代码($WA 20$)


#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<map> #define min(x,y) ((x)<(y)?(x):(y)) #define Maxn 200050 #define Reg register using namespace std; int biao[Maxn]; int t,n,m,tot,top,ans,ok,fir[Maxn],fic[Maxn],vis[Maxn]; int root,indx,cnt,dfn[Maxn],low[Maxn],cur[Maxn],stack[Maxn],pos[Maxn],num[Maxn]; struct Tu {int st,ed,next;} lian[Maxn*2],liap[Maxn*2]; vector<vector<int> > dcc(Maxn/10); void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=fir[x]; fir[x]=tot; return; } void adp(int x,int y) { liap[++top].st=x; liap[top].ed=y; liap[top].next=fic[x]; fic[x]=top; return; } void tarjan(int x,int fat) { dfn[x]=low[x]=++indx; if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;} stack[++stack[0]]=x; int flag=0; for(Reg int i=fir[x];i;i=lian[i].next) { if(!dfn[lian[i].ed]) { tarjan(lian[i].ed,x); low[x]=min(low[x],low[lian[i].ed]); if(low[lian[i].ed]>=dfn[x]) { ++flag; if(flag>0||x!=root) cur[x]=1; ++cnt; while(stack[stack[0]]!=lian[i].ed) dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(x); } } else if(lian[i].ed!=fat) low[x]=min(low[x],dfn[lian[i].ed]); } return; } void dfs(int x) { vis[x]=1; if(x==pos[n]) {ok=1; return;} if(ok) return; for(Reg int i=fic[x];i;i=liap[i].next) { if(!vis[liap[i].ed]) { if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed]; dfs(liap[i].ed); if(!ok) --stack[0]; else return; } } return; } void init() { tot=top=ans=ok=root=indx=cnt=stack[0]=0; for(Reg int x=1;x<=n;++x) { biao[x]=fir[x]=fic[x]=cur[x]=vis[x]=dfn[x]=low[x]=pos[x]=num[x]=0; if(x<Maxn) dcc[x].clear(); } return; } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); for(Reg int i=1,x,y;i<=m;++i) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(Reg int i=1;i<=n;++i) if(!dfn[i]) root=i,tarjan(i,i); int num=cnt; for(Reg int i=1;i<=n;++i) if(cur[i]) pos[i]=++num,biao[num]=i; for(Reg int i=1,l;i<=cnt;++i) { l=dcc[i].size(); for(Reg int j=0,x;j<l;++j) { x=dcc[i][j]; if(cur[x]) {adp(pos[x],i); adp(i,pos[x]);} else pos[x]=i; } } cnt=num; stack[0]=0; dfs(pos[1]); memset(vis,0,sizeof(vis)); ans=0; for(Reg int i=1;i<=stack[0];++i) { if(!vis[stack[i]]&&stack[i]!=n&&stack[i]!=1) { ++ans; vis[stack[i]]=1; } } printf("%d\n",ans); for(Reg int i=1;i<=n;++i) if(vis[i]) printf("%d ",i); printf("\n"); } return 0; }
$AC$代码


#include<iostream> #include<cstring> #include<cstdio> #include<vector> #define min(x,y) ((x)<(y)?(x):(y)) #define Maxn 200050 #define Reg register using namespace std; int biao[Maxn*5]; int t,n,m,tot,top,ans,ok,fir[Maxn*5],fic[Maxn*5],vis[Maxn*5]; int root,indx,cnt,dfn[Maxn*5],low[Maxn*5],cur[Maxn],stack[Maxn*5],pos[Maxn*5]; struct Tu {int st,ed,next;} lian[Maxn*8],liap[Maxn*8]; vector<vector<int> > dcc(Maxn); void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=fir[x]; fir[x]=tot; return; } void adp(int x,int y) { liap[++top].st=x; liap[top].ed=y; liap[top].next=fic[x]; fic[x]=top; return; } void tarjan(int x) { dfn[x]=low[x]=++indx; stack[++stack[0]]=x; if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;} int flag=0; for(Reg int i=fir[x];i;i=lian[i].next) { if(!dfn[lian[i].ed]) { tarjan(lian[i].ed); low[x]=min(low[x],low[lian[i].ed]); if(low[lian[i].ed]>=dfn[x]) { ++flag; if(flag>0||x!=root) cur[x]=1; ++cnt; while(stack[stack[0]]!=lian[i].ed) dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(x); } } else low[x]=min(low[x],dfn[lian[i].ed]); } return; } void dfs(int x) { vis[x]=1; if(x==pos[n]) {ok=1; return;} if(ok) return; for(Reg int i=fic[x];i;i=liap[i].next) { if(!vis[liap[i].ed]) { if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed]; dfs(liap[i].ed); if(biao[liap[i].ed]&&!ok) --stack[0]; if(ok) return; } } return; } void init() { tot=top=ans=ok=root=indx=cnt=stack[0]=0; for(Reg int x=0;x<=n*2;++x) biao[x]=fir[x]=fic[x]=cur[x]=vis[x]=dfn[x]=low[x]=pos[x]=0; return; } int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); for(Reg int i=1,x,y;i<=m;++i) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(Reg int i=1;i<=n;++i) if(!dfn[i]) root=i,tarjan(i); int num=cnt; for(Reg int i=1;i<=n;++i) if(cur[i]) {pos[i]=++num; biao[num]=i;} for(Reg int i=1,l;i<=cnt;++i) { l=dcc[i].size(); for(Reg int j=0,x;j<l;++j) { x=dcc[i][j]; if(cur[x]) {adp(pos[x],i); adp(i,pos[x]);} else pos[x]=i; } dcc[i].clear(); } // cout<<endl; // for(Reg int i=1;i<=n;++i) cout<<pos[i]<<' '; // cout<<endl; cnt=num; stack[0]=0; ok=0; dfs(pos[1]); memset(vis,0,sizeof(vis)); ans=0; for(Reg int i=1;i<=stack[0];++i) { if(!vis[stack[i]]&&stack[i]!=n&&stack[i]!=1) { ++ans; vis[stack[i]]=1; } } printf("%d\n",ans); for(Reg int i=1;i<=n;++i) if(vis[i]) printf("%d ",i); printf("\n"); } return 0; }
低错总结:
1、数组没开够。
2、板子不熟练,考试调$Tarjan$调了半小时,然后最后还没打对。
$Tarjan$找割点的板子
void tarjan(int x) { dfn[x]=low[x]=++indx; stack[++stack[0]]=x; if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;} int flag=0; for(Reg int i=fir[x];i;i=lian[i].next) { if(!dfn[lian[i].ed]) { tarjan(lian[i].ed); low[x]=min(low[x],low[lian[i].ed]); if(low[lian[i].ed]>=dfn[x]) { ++flag; if(flag>1||x!=root) cur[x]=1; ++cnt; while(stack[stack[0]]!=lian[i].ed) dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(x); } } else low[x]=min(low[x],dfn[lian[i].ed]); } return; }
考试时码的:
void tarjan(int x,int fat) { dfn[x]=low[x]=++indx; if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;} stack[++stack[0]]=x; int flag=0; for(Reg int i=fir[x];i;i=lian[i].next) { if(!dfn[lian[i].ed]) { tarjan(lian[i].ed,x); low[x]=min(low[x],low[lian[i].ed]); if(low[lian[i].ed]>=dfn[x]) { ++flag; if(flag>0||x!=root) cur[x]=1; ++cnt; while(stack[stack[0]]!=lian[i].ed) dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(stack[stack[0]--]); dcc[cnt].push_back(x); } } else if(lian[i].ed!=fat) low[x]=min(low[x],dfn[lian[i].ed]); } return; }
3、
void dfs(int x) { vis[x]=1; if(x==pos[n]) {ok=1; return;} if(ok) return; for(Reg int i=fic[x];i;i=liap[i].next) { if(!vis[liap[i].ed]) { if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed]; dfs(liap[i].ed); if(!ok) --stack[0]; else return; } } return; }
这个地方的弹栈弹过了,出现负数。。
改了一点:
if(biao[liap[i].ed]&&!ok) --stack[0]; if(ok) return;
然后过了。。
C. 寿司
$O(n^2)$的算法:
预处理出$sum[i]$表示$i$节点之前$1$的个数。
枚举区间和断点,
对于区间$[L,R]$,假设断点是$Mid$,
那么$Mid$左侧的$1$都应该移到最左端,右侧的$1$都应该移到最右端,
那么左侧需要移动的步数就是$W_1=\sum \limits_{i=L,num[i]=1}^{Mid} i-(L+sum[i]-sum[L]-1)$
右侧需要移动的步数就是$W_2=\sum \limits_{i=Mid+1,num[i]=1}^{R} R-sum[R]+sum[i-1]+1-i$
现在复杂度是$O(n^3)$,
然后化简一下就可以发现可以用前缀和。
最后就是$O(n^2)$的复杂度。
$O(n)$的算法:
决策单调性。。
当区间右移的时候,最优断点不可能左移。
至于证明:
有一个数学的方法,有点麻烦。
在$n^2$的算法中我们能够化出一个式子:
另$clac[i]$表示在当前状态中$i$点的最优步数,
那么$clac[i]= K\times sum[i] +sum1[i] -sum2[i]+P$,
其中
$sum1[i]=\sum \limits_{j=1}^{i} (j-sum[j])$
$sum2[i]=\sum \limits_{j=1}^{i} (sum[j-1]-j)$
$P=sum2[R]-sum1[L-1]+(R-sum[R]+1)\times sum[R]-(sum[L-1]-L+1)\times sum[L-1]$
$K=sum[L-1]+sum[R]-(L+R)$。
令在区间$[L,R]$中断点为$mid$时最优,
那么肯定在$[L,R]$中$clac[mid]<=clac[mid-1]$,
也就是:
$K\times sum[mid-1]+sum1[mid-1]-sum2[mid-1]+P>=K\times sum[mid] +sum[mid]-sum2[mid]+P$
再移个项:
$(K-1)\times num[mid]-2\times sum[mid]+2\times mid<=0$ ---------(*),
我们要证明在区间$[L+1,R+1]$中仍然满足这个性质:
接下来这个$P$可以消掉,不管它,看$K$。
当$L++,R++$时,
$K'=sum[L]+sum[R+1]-(L+R)-2$,
$K'=sum[L-1]+sum[R]-(L+R)-2+num[L]+num[R+1]$,
显然这个$K'<=K$,那么上边的(*)式子仍然满足。
所以就得出来了。
丑陋的代码:


#include<iostream> #include<cstring> #include<cstdio> #define min(x,y) ((x)<(y)?(x):(y)) #define int long long #define INF 0x7fffffffffffff #define Maxn 4000050 #define Reg register using namespace std; int t,n,P,K,mid,ans,L,R; int sum[Maxn],sum1[Maxn],sum2[Maxn],num[Maxn]; char A[Maxn]; int clac(int x) {return K*sum[x]+sum1[x]-sum2[x]+P;} signed main() { scanf("%lld",&t); while(t--) { ans=INF; scanf("%s",A); n=strlen(A); sum[0]=sum1[0]=sum2[0]=0; for(Reg int i=1;i<=n;++i) { sum[i]=sum[i-1]; sum1[i]=sum1[i-1]; sum2[i]=sum2[i-1]; if(A[i-1]=='R') { ++sum[i]; num[i]=1; sum1[i]+=i-sum[i]; sum2[i]+=sum[i-1]-i; } else num[i]=0; } for(Reg int i=n+1;i<=2*n;++i) { num[i]=num[i-n]; sum[i]=sum[i-1]; sum1[i]=sum1[i-1]; sum2[i]=sum2[i-1]; if(num[i]==1) { ++sum[i]; sum1[i]+=i-sum[i]; sum2[i]+=sum[i-1]-i; } } L=1,R=n; P=sum2[R]-sum1[L-1]+(R-sum[R]+1)*sum[R]-(sum[L-1]-L+1)*sum[L-1]; K=sum[L-1]-L+1-R+sum[R]-1; mid=0; for(Reg int i=1;i<=n;++i) //枚举左端点 { L=i,R=i+n-1; P=sum2[R]-sum1[L-1]+(R-sum[R]+1)*sum[R]-(sum[L-1]-L+1)*sum[L-1]; K=sum[L-1]-L+1-R+sum[R]-1; ans=min(ans,clac(mid)); while(mid+1<=R&&clac(mid+1)<=clac(mid)) //决策单调性处理 { ans=min(ans,clac(mid+1)); ++mid; } } printf("%lld\n",ans); } return 0; }
当然也有更简单的证法:
$wd$大神题解,传送门
总结:
$T1$第一眼看是$KMP$,然后不会打。。。
打了一个$Hash$,然后$WA18$。
觉得自己之前打的是个假的$Hash$,
$T2$基本上是个$Tarjan$的板子,然后$WA20$,
这次$T1$和$T2$基本上都是送分题,然后就爆炸了。
板子不会,代码能力差,低错。。。。
然后最后$18+20+0=38$,
没什么水平。。。
最后忠告:
$AC$一道题需要$100+$行的代码,然而从$AC$到$WA$仅仅需要$1$个字符。
所以一定要打对拍。。