HEOI2018题解
得知了一切真相,弄清了一切是怎么发生的之后,可能没那么伤心了(假的,我更伤心了QAQ)。不能否认是一套非常棒的题目吧,应该是近几届质量最高的HEOI了,将来或许也很难超越。
Day1
T1《一双木棋》
得分:50
估分与得分一致,一致地低。没有梦想?没想过在省选A题,尽管HEOI2017的Day1T1都差点过了……也是因为一开始不知道怎么搜,后来会搜了之后就觉得可以了,没去尝试进一步得分。思路上主要是没有想到可以反向处理,显然正着做是只能枚举没有什么优化余地的。看上去很高端但是和博弈论并没有什么关系呢。
可以发现任何时候剩下的部分一定是一个右下角,用一个轮廓线表示当前状态(1是向上,0是向右),可以用数组记录从这往后的最优解。在转移的不同步数需要取min或max,而哪个地方可以转移是很显然的。相当好写,跑得飞快。好像同学们直接反向暴搜hash/map记忆化一下就过了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define inf 0x7fffffff 6 using namespace std; 7 const int sj=15; 8 int n,m,tot,a[sj][sj],b[sj][sj]; 9 bool v[sj][sj],h[sj][sj]; 10 inline int maxx(int x,int y) 11 { 12 return x>y?x:y; 13 } 14 inline int minn(int x,int y) 15 { 16 return x<y?x:y; 17 } 18 int dfs(int op,int sum) 19 { 20 int ret=0; 21 if(sum==tot) 22 { 23 for(int i=1;i<=n;i++) 24 for(int j=1;j<=m;j++) 25 { 26 if(h[i][j]) ret+=a[i][j]; 27 else ret-=b[i][j]; 28 } 29 return ret; 30 } 31 if(op==1) 32 { 33 ret=-inf; 34 for(int i=1;i<=n;i++) 35 for(int j=1;j<=m;j++) 36 if(!v[i][j]&&(i==1||v[i-1][j])&&(j==1||v[i][j-1])) 37 { 38 v[i][j]=h[i][j]=1; 39 ret=maxx(ret,dfs(0,sum+1)); 40 v[i][j]=h[i][j]=0; 41 } 42 } 43 else 44 { 45 ret=inf; 46 for(int i=1;i<=n;i++) 47 for(int j=1;j<=m;j++) 48 if(!v[i][j]&&(i==1||v[i-1][j])&&(j==1||v[i][j-1])) 49 { 50 v[i][j]=1,h[i][j]=0; 51 ret=minn(ret,dfs(1,sum+1)); 52 v[i][j]=h[i][j]=0; 53 } 54 } 55 return ret; 56 } 57 int main() 58 { 59 //freopen("chess1.in","r",stdin); 60 freopen("chess.in","r",stdin); 61 freopen("chess.out","w",stdout); 62 scanf("%d%d",&n,&m); 63 tot=n*m; 64 for(int i=1;i<=n;i++) 65 for(int j=1;j<=m;j++) 66 scanf("%d",&a[i][j]); 67 for(int i=1;i<=n;i++) 68 for(int j=1;j<=m;j++) 69 scanf("%d",&b[i][j]); 70 printf("%d",dfs(1,0)); 71 fclose(stdin);fclose(stdout); 72 return 0; 73 }
1 #include<iostream> 2 #include<cstdio> 3 #define inf 0x7fffffff 4 using namespace std; 5 const int sj=12; 6 int n,m,a[sj][sj],b[sj][sj],bin[sj<<1],s,f[1<<21]; 7 bool vi[1<<21]; 8 inline int maxx(int x,int y) 9 { 10 return x>y?x:y; 11 } 12 inline int minn(int x,int y) 13 { 14 return x<y?x:y; 15 } 16 int dfs(int zt,int op) 17 { 18 if(vi[zt]) return f[zt]; 19 vi[zt]=1; 20 if(op) f[zt]=inf; 21 else f[zt]=-inf; 22 int a1=0,a2=0; 23 if(zt&bin[0]) a1++; 24 else a2++; 25 for(int i=0;i<n+m-1;i++) 26 { 27 if(zt&bin[i+1]) a1++; 28 else a2++; 29 if((zt&bin[i])&&!(zt&bin[i+1])) 30 { 31 s=zt-bin[i]+bin[i+1]; 32 if(op) f[zt]=minn(f[zt],dfs(s,op^1)-b[n-a1+1][a2]); 33 else f[zt]=maxx(f[zt],dfs(s,op^1)+a[n-a1+1][a2]); 34 } 35 } 36 return f[zt]; 37 } 38 int main() 39 { 40 scanf("%d%d",&n,&m); 41 for(int i=1;i<=n;i++) 42 for(int j=1;j<=m;j++) 43 scanf("%d",&a[i][j]); 44 for(int i=1;i<=n;i++) 45 for(int j=1;j<=m;j++) 46 scanf("%d",&b[i][j]); 47 bin[0]=1; 48 for(int i=1;i<=n+m;i++) bin[i]=bin[i-1]<<1; 49 for(int i=1;i<=n;i++) s+=bin[i-1+m]; 50 vi[s]=1,s=0; 51 for(int i=1;i<=n;i++) s+=bin[i-1]; 52 printf("%d",dfs(s,0)); 53 return 0; 54 }
T2《iiidx》
得分:60
Day1主要的时间都给了这道题……得分不是很低,但实际上是大众分,并且剩下两题得分都不高。贪心的思路比较显然,在有相同权值时的锅也比较显然。因为我前面是直接把区间向下分治的,所以总是想着通过调整序列什么的改正这个做法,想了很久也没有进展。实际上我们并不能在父亲处就确定子树控制的区间(会出现我所担心的“兄弟并没有变大,儿子却变小了”的情况),所以正解的方向是数据结构维护。
为了字典序最大要逐位确定,先把序列降序排列。因为要保证子树里有足够的点,可以在处理每个点时把子树的大小预订出来。每个点维护左侧还有多少可行点,预订时就从当前点往右把预订值都减去,用线段树区间减实现。对于每个点,可行的位置需要满足右边所有点的权值都不小于它的size,在这些位置中选值最大的。所以线段树维护的是区间最小值,查询后在同样的数里选最靠右的作为更新位置。处理每个点子树中第一个点时应该把它的预订值消掉,不过父亲节点被选走造成的影响是不能消的。官方题解给出的例子非常清楚。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int sj=500010; 8 int n,d[sj],h[sj],e,size[sj],fa,ans[sj]; 9 double k; 10 struct B 11 { 12 int ne,v; 13 }b[sj]; 14 void add(int x,int y) 15 { 16 b[e].v=y,b[e].ne=h[x],h[x]=e++; 17 } 18 inline int read() 19 { 20 int jg=0,jk=getchar()-'0'; 21 while(jk<0||jk>9) jk=getchar()-'0'; 22 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 23 return jg; 24 } 25 void dfs(int x) 26 { 27 size[x]=1; 28 for(int i=h[x];i!=-1;i=b[i].ne) 29 dfs(b[i].v),size[x]+=size[b[i].v]; 30 } 31 void solve(int x,int l,int r) 32 { 33 //cout<<x<<" "<<l<<" "<<r<<endl; 34 ans[x]=d[r]; 35 if(l==r) return; 36 int pr=0; 37 for(int i=h[x];i!=-1;i=b[i].ne) 38 { 39 solve(b[i].v,l+pr,l+pr+size[b[i].v]-1); 40 pr+=size[b[i].v]; 41 } 42 } 43 int main() 44 { 45 //freopen("iiidx1.in","r",stdin); 46 //freopen("me.out","w",stdout); 47 freopen("iiidx.in","r",stdin); 48 freopen("iiidx.out","w",stdout); 49 scanf("%d%lf",&n,&k); 50 for(int i=1;i<=n;i++) scanf("%d",&d[i]); 51 sort(d+0,d+n+1,greater<int>()); 52 memset(h,-1,sizeof(h)); 53 for(int i=n;i>=1;i--) 54 { 55 fa=floor(i/k); 56 add(fa,i); 57 //cout<<i<<" "<<fa<<endl; 58 } 59 dfs(0); 60 solve(0,0,n); 61 for(int i=1;i<n;i++) printf("%d ",ans[i]); 62 printf("%d",ans[n]); 63 fclose(stdin);fclose(stdout); 64 return 0; 65 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define inf 0x7fffffff 7 using namespace std; 8 const int sj=500010; 9 int n,d[sj],h[sj],e,size[sj],fa[sj],ans[sj],bd[sj]; 10 bool vi[sj]; 11 double k; 12 struct B 13 { 14 int ne,v; 15 }b[sj]; 16 struct tree 17 { 18 int l,r,v,lz; 19 }t[sj<<2]; 20 inline int read() 21 { 22 int jg=0,jk=getchar()-'0'; 23 while(jk<0||jk>9) jk=getchar()-'0'; 24 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 25 return jg; 26 } 27 void dfs(int x) 28 { 29 size[x]=1; 30 for(int i=h[x];i!=-1;i=b[i].ne) 31 dfs(b[i].v),size[x]+=size[b[i].v]; 32 } 33 inline int minn(int x,int y){ return x<y?x:y; } 34 inline int maxx(int x,int y){ return x>y?x:y; } 35 void build(int x,int z,int y) 36 { 37 t[x].l=z,t[x].r=y; 38 if(z==y) 39 { 40 t[x].v=z; 41 return; 42 } 43 int mid=z+y>>1; 44 build(x<<1,z,mid),build(x<<1|1,mid+1,y); 45 t[x].v=minn(t[x<<1].v,t[x<<1|1].v); 46 } 47 void pushdown(int x) 48 { 49 if(t[x].lz) 50 { 51 t[x<<1].v+=t[x].lz; 52 t[x<<1].lz+=t[x].lz; 53 t[x<<1|1].v+=t[x].lz; 54 t[x<<1|1].lz+=t[x].lz; 55 t[x].lz=0; 56 } 57 } 58 void update(int x,int pos,int sum) 59 { 60 if(t[x].l==pos) 61 { 62 t[x].lz+=sum,t[x].v+=sum; 63 return; 64 } 65 pushdown(x); 66 if(t[x<<1|1].l<=pos) update(x<<1|1,pos,sum); 67 else 68 { 69 t[x<<1|1].lz+=sum,t[x<<1|1].v+=sum; 70 update(x<<1,pos,sum); 71 } 72 t[x].v=minn(t[x<<1].v,t[x<<1|1].v); 73 } 74 int query(int x,int sum) 75 { 76 if(t[x].l==t[x].r) 77 { 78 if(t[x].v>=sum) return d[t[x].l]; 79 return -inf; 80 } 81 pushdown(x); 82 if(t[x<<1|1].v>=sum) return maxx(d[t[x<<1|1].l],query(x<<1,sum)); 83 return query(x<<1|1,sum); 84 } 85 int getpos(int sum) 86 { 87 int le=1,ri=n,mid; 88 while(le<ri) 89 { 90 mid=(le+ri+1)>>1; 91 if(d[mid]>=sum) le=mid; 92 else ri=mid-1; 93 } 94 return le; 95 } 96 int main() 97 { 98 scanf("%d%lf",&n,&k); 99 for(int i=1;i<=n;i++) scanf("%d",&d[i]); 100 sort(d+1,d+n+1,greater<int>()); 101 memset(h,-1,sizeof(h)); 102 for(int i=n;i>=1;i--) 103 { 104 fa[i]=floor(i/k); 105 b[e].v=i,b[e].ne=h[fa[i]],h[fa[i]]=e++; 106 } 107 dfs(0),build(1,1,n); 108 for(int i=1;i<=n;i++) 109 { 110 if(vi[fa[i]]) update(1,bd[fa[i]],size[fa[i]]-1),vi[fa[i]]=0; 111 ans[i]=query(1,size[i]),bd[i]=getpos(ans[i]); 112 update(1,bd[i],-size[i]),vi[i]=1; 113 } 114 for(int i=1;i<n;i++) printf("%d ",ans[i]); 115 printf("%d",ans[n]); 116 return 0; 117 }
T3《秘密袭击》
得分:25
想这题的时间不多,只写了枚举和链上部分。因为数据范围比较小被暴力水过了……我还不会正解,大概是拉格朗日插值模拟NTT过程优化背包什么的,感觉这题有点可惜啊。或许出题人能想到的暴力方法总是带着正解的影子,卡掉所有暴力实在太难了吧。
复杂度大概在$n^3$的暴力有两种思路。一是像正解一样转化成“$k$大值大于等于某个值的联通块个数”,需要枚举这个值做背包,统计答案时累加DP值即可,优化的空间不是很大,但是跑得快的OJ也可以过。二是直接求$k$大值等于某个值的联通块数乘权值统计答案,这又有两种实现方法。用$f[i][j]$表示$i$为根的子树有$j$个值大于等于当前处理的值方案数。如果枚举每个点求它作为$k$大的方案数,可以忽略$f[i][k]$以上的值。如果从大到小枚举每个权值逐个加入树求至少包含$k$个已加入点的背包数,可以通过差分得到这个权值对应的方案数;这种做法能优化的地方很多,比如把大于$k$的方案全都累加到$k$上、DP之前判断这种权值有没有出现过、大于等于它的数是否不少于$k$个等等,实际的复杂度是$nk(n-k)$的,几乎什么OJ都能过。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int sj=1700; 7 const int mod=64123; 8 int n,k,w,d[sj],h[sj],e,deg[sj],mx,tot,cnt,iu[sj],rt[sj],ans; 9 int a1,a2,bin[25]; 10 11 bool vi[25],ned[25]; 12 struct B 13 { 14 int ne,v,w; 15 }b[sj<<1]; 16 void add(int x,int y) 17 { 18 b[e].v=y,b[e].ne=h[x],h[x]=e++; 19 b[e].v=x,b[e].ne=h[y],h[y]=e++; 20 } 21 inline int read() 22 { 23 int jg=0,jk=getchar()-'0'; 24 while(jk<0||jk>9) jk=getchar()-'0'; 25 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 26 return jg; 27 } 28 inline int maxx(int x,int y) 29 { 30 return x>y?x:y; 31 } 32 void tar(int x,int fx) 33 { 34 iu[++tot]=d[x]; 35 for(int i=h[x];i!=-1;i=b[i].ne) 36 if(b[i].v!=fx) 37 tar(b[i].v,x); 38 } 39 struct tree 40 { 41 int lc,rc,sum; 42 }t[sj*50]; 43 void insert(int la,int nt,int l,int r,int pos) 44 { 45 t[nt].lc=t[la].lc,t[nt].rc=t[la].rc,t[nt].sum=t[la].sum+1; 46 if(l==r) return; 47 int mid=l+r>>1; 48 if(pos<=mid) t[nt].lc=++cnt,insert(t[la].lc,t[nt].lc,l,mid,pos); 49 else t[nt].rc=++cnt,insert(t[la].rc,t[nt].rc,mid+1,r,pos); 50 } 51 int query(int la,int nt,int l,int r,int v) 52 { 53 if(l==r) return l; 54 int mid=l+r>>1,tp=t[t[nt].rc].sum-t[t[la].rc].sum; 55 if(tp>=v) return query(t[la].rc,t[nt].rc,mid+1,r,v); 56 else return query(t[la].lc,t[nt].lc,l,mid,v-tp); 57 } 58 void work1() 59 { 60 for(int i=1;i<=n;i++) 61 if(deg[i]==1) 62 { 63 tar(i,0); 64 break; 65 } 66 for(int i=1;i<=n;i++) 67 rt[i]=++cnt,insert(rt[i-1],rt[i],1,w,iu[i]); 68 for(int i=1;i<=n;i++) 69 for(int j=i+k-1;j<=n;j++) 70 ans=(ans+query(rt[i-1],rt[j],1,w,k))%mod; 71 printf("%d",ans); 72 } 73 void dfs(int x) 74 { 75 vi[x]=1,cnt++; 76 for(int i=h[x];i!=-1;i=b[i].ne) 77 if(ned[b[i].v]&&!vi[b[i].v]) 78 dfs(b[i].v); 79 } 80 void work2() 81 { 82 bin[0]=1; 83 for(int i=1;i<=n;i++) bin[i]=bin[i-1]<<1; 84 for(int i=1;i<bin[n];i++) 85 { 86 tot=cnt=0; 87 memset(ned,0,sizeof(ned)); 88 for(int j=1;j<=n;j++) 89 if(i&bin[j-1]) 90 { 91 iu[++tot]=d[j]; 92 mx=j,ned[j]=1; 93 } 94 if(tot<k) continue; 95 memset(vi,0,sizeof(vi)); 96 dfs(mx); 97 if(cnt!=tot) continue; 98 sort(iu+1,iu+tot+1,greater<int>()); 99 ans=(ans+iu[k])%mod; 100 } 101 printf("%d",ans); 102 } 103 int main() 104 { 105 //freopen("coat3.in","r",stdin); 106 freopen("coat.in","r",stdin); 107 freopen("coat.out","w",stdout); 108 n=read(),k=read(),w=read(); 109 memset(h,-1,sizeof(h)); 110 for(int i=1;i<=n;i++) d[i]=read(); 111 for(int i=1;i<n;i++) 112 { 113 a1=read(),a2=read(); 114 add(a1,a2); 115 deg[a1]++,deg[a2]++; 116 mx=maxx(mx,deg[a1]); 117 mx=maxx(mx,deg[a2]); 118 } 119 if(mx<=2) work1(); 120 else work2(); 121 //work1(); 122 //work2(); 123 fclose(stdin);fclose(stdout); 124 return 0; 125 }
1 #pragma GCC optimize("-O3") 2 #include<cstdio> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 inline int read() 7 { 8 int jg=0,jk=getchar()-'0'; 9 while(jk<0||jk>9) jk=getchar()-'0'; 10 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 11 return jg; 12 } 13 const int sj=1700; 14 const int mod=64123; 15 int n,k,w,d[sj],h[sj],e,size[sj],tot,tmp[sj],sum[sj],g[sj]; 16 ll f[sj][sj],ans; 17 struct B 18 { 19 int ne,v; 20 }b[sj<<1]; 21 inline void add(int x,int y) 22 { 23 b[e].v=y,b[e].ne=h[x],h[x]=e++; 24 b[e].v=x,b[e].ne=h[y],h[y]=e++; 25 } 26 inline int minn(int x,int y) 27 { 28 return x<y?x:y; 29 } 30 inline void dfs(int x,int fx,int y) 31 { 32 if(d[x]>=y) size[x]=1; 33 else size[x]=0; 34 memset(f[x],0,sizeof(ll)*(k+1)); 35 f[x][size[x]]=1; 36 register int i,j,l; 37 for(i=h[x];i!=-1;i=b[i].ne) 38 if(b[i].v!=fx) 39 { 40 dfs(b[i].v,x,y); 41 for(j=0;j<=size[x];++j) tmp[j]=f[x][j]; 42 for(j=0;j<=size[x];++j) 43 for(l=0;l<=size[b[i].v];l++) 44 f[x][minn(j+l,k)]=(f[x][minn(j+l,k)]+f[b[i].v][l]*tmp[j])%mod; 45 size[x]+=size[b[i].v]; 46 if(size[x]>k) size[x]=k; 47 } 48 g[y]=(g[y]+f[x][k])%mod; 49 } 50 int main() 51 { 52 n=read(),k=read(),w=read(); 53 memset(h,-1,sizeof(h)); 54 for(int i=1;i<=n;++i) d[i]=read(),sum[d[i]]++; 55 for(int i=1;i<n;++i) add(read(),read()); 56 for(int i=w;i>=1;--i) sum[i]+=sum[i+1]; 57 for(register int i=w;i>=1;--i) 58 { 59 if(sum[i]<k) continue; 60 if(sum[i]==sum[i+1]) 61 { 62 g[i]=g[i+1]; 63 continue; 64 } 65 dfs(1,0,i); 66 ans=(ans+(g[i]-g[i+1]+mod)*i)%mod; 67 } 68 printf("%lld",ans); 69 return 0; 70 }
Day2
T1《劈配》
得分:0
Day2唯一一道容易得分的题目,因为判一个导师是否是选手前$s_i$个志愿时没有判0挂掉60分爆零了……难以理解为什么能过全部的样例,而这道题又确实不知道应该怎么拍……出题人不管暴力还是正解的思路似乎都是得到第一问再把第二问转成第一问验证,但这正是我考场上想要避免的。可以发现$c=1$的部分每个选手的决策是唯一的,既然如此只要记一下每个导师在每个选手处已招了多少学员就可以直接判断这个位置能否满足梦想,或许是因为数据水可以拿到60分。
正解是最大流逐步加边。从源点向选手连容量为1的边,从导师向汇点连容量为$b_i$的边。依次枚举每个选手的每个志愿,从选手向导师连边看能否匹配成功,成功就得到第一问的解,不成功要把在这个志愿加的边删掉,即回到上一个选手匹配结束的状态。需要记录每个选手匹配结束后的残量网络,对于第二问二分并找到对应的上一位选手的残量网络加边,再把这位选手的前$s_i$个志愿都加边看能否匹配成功。这道题深刻地告诉我们,网络流不仅dinic要有信仰,建图也要有信仰,听上去复杂度爆炸的一些操作未必是不可行的。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 inline int read() 6 { 7 int jg=0,jk=getchar()-'0'; 8 while(jk<0||jk>9) jk=getchar()-'0'; 9 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 10 return jg; 11 } 12 const int sj=210; 13 int n,m,b[sj],a[sj][sj],s[sj],ca,c,ans1[sj],ans2[sj],sum[sj][sj]; 14 int zs[sj][sj],opt[sj]; 15 void init() 16 { 17 n=read(),m=read(); 18 for(int i=1;i<=m;i++) b[i]=read(); 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=m;j++) 21 a[i][j]=read(); 22 for(int i=1;i<=n;i++) s[i]=read(); 23 } 24 void chec() 25 { 26 for(int i=1;i<=n;i++) 27 if(ans1[i]<opt[i]) 28 return; 29 for(int i=1;i<=n;i++) 30 ans1[i]=opt[i]; 31 memcpy(zs,sum,sizeof(sum)); 32 } 33 void dfs(int x) 34 { 35 if(x==n+1) 36 { 37 chec(); 38 return; 39 } 40 bool op; 41 for(int j=1;j<=m;j++) sum[x][j]=sum[x-1][j]; 42 for(int i=1;i<=ans1[x];i++) 43 { 44 op=0; 45 for(int j=1;j<=m;j++) 46 if(a[x][j]==i&&sum[x][j]<b[j]) 47 { 48 sum[x][j]++,opt[x]=i,op=1; 49 //cout<<"* "<<x<<" "<<i<<" "<<j<<endl; 50 dfs(x+1); 51 sum[x][j]--,opt[x]=0; 52 } 53 if(op) break; 54 } 55 if(!op) opt[x]=m+1,dfs(x+1); 56 } 57 void work1() 58 { 59 memset(opt,0,sizeof(opt)); 60 memset(ans2,0,sizeof(ans2)); 61 for(int i=1;i<=n;i++) ans1[i]=m; 62 dfs(1); 63 for(int i=1;i<n;i++) printf("%d ",ans1[i]); 64 printf("%d\n",ans1[n]); 65 for(int i=1;i<=n;i++) 66 { 67 if(ans1[i]<=s[i]) continue; 68 for(int j=1;j<i;j++) 69 { 70 for(int k=1;k<=m;k++) 71 if(a[i][k]<=s[i]&&zs[i-j-1][k]<b[k]) 72 ans2[i]=j; 73 if(ans2[i]) break; 74 } 75 if(!ans2[i]) ans2[i]=i; 76 } 77 for(int i=1;i<n;i++) printf("%d ",ans2[i]); 78 printf("%d\n",ans2[n]); 79 } 80 int main() 81 { 82 //freopen("mentor4.in","r",stdin); 83 //freopen("me.out","w",stdout); 84 freopen("mentor.in","r",stdin); 85 freopen("mentor.out","w",stdout); 86 ca=read(),c=read(); 87 for(int l=1;l<=ca;l++) 88 { 89 init(); 90 work1(); 91 } 92 fclose(stdin);fclose(stdout); 93 return 0; 94 }
1 clude<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 inline int read() 6 { 7 int jg=0,jk=getchar()-'0'; 8 while(jk<0||jk>9) jk=getchar()-'0'; 9 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 10 return jg; 11 } 12 const int sj=210; 13 int n,m,b[sj],a[sj][sj],s[sj],ca,c,ans1[sj],ans2[sj],sum[sj][sj]; 14 int zs[sj][sj],opt[sj]; 15 void init() 16 { 17 n=read(),m=read(); 18 for(int i=1;i<=m;i++) b[i]=read(); 19 for(int i=1;i<=n;i++) 20 for(int j=1;j<=m;j++) 21 a[i][j]=read(); 22 for(int i=1;i<=n;i++) s[i]=read(); 23 } 24 void chec() 25 { 26 for(int i=1;i<=n;i++) 27 if(ans1[i]<opt[i]) 28 return; 29 for(int i=1;i<=n;i++) 30 ans1[i]=opt[i]; 31 memcpy(zs,sum,sizeof(sum)); 32 } 33 void dfs(int x) 34 { 35 if(x==n+1) 36 { 37 chec(); 38 return; 39 } 40 bool op; 41 for(int j=1;j<=m;j++) sum[x][j]=sum[x-1][j]; 42 for(int i=1;i<=ans1[x];i++) 43 { 44 op=0; 45 for(int j=1;j<=m;j++) 46 if(a[x][j]==i&&sum[x][j]<b[j]) 47 { 48 sum[x][j]++,opt[x]=i,op=1; 49 dfs(x+1); 50 sum[x][j]--,opt[x]=0; 51 } 52 if(op) break; 53 } 54 if(!op) opt[x]=m+1,dfs(x+1); 55 } 56 void work1() 57 { 58 memset(opt,0,sizeof(opt)); 59 memset(ans2,0,sizeof(ans2)); 60 for(int i=1;i<=n;i++) ans1[i]=m+1; 61 dfs(1); 62 for(int i=1;i<n;i++) printf("%d ",ans1[i]); 63 printf("%d\n",ans1[n]); 64 for(int i=1;i<=n;i++) 65 { 66 if(ans1[i]<=s[i]) continue; 67 for(int j=1;j<i;j++) 68 { 69 for(int k=1;k<=m;k++) 70 if(a[i][k]&&a[i][k]<=s[i]&&zs[i-j-1][k]<b[k]) 71 ans2[i]=j; 72 if(ans2[i]) break; 73 } 74 if(!ans2[i]) ans2[i]=i; 75 } 76 for(int i=1;i<n;i++) printf("%d ",ans2[i]); 77 printf("%d\n",ans2[n]); 78 } 79 int main() 80 { 81 ca=read(),c=read(); 82 for(int l=1;l<=ca;l++) 83 { 84 init(); 85 work1(); 86 } 87 return 0; 88 }
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 #define inf 0x7fffffff 6 using namespace std; 7 inline int read() 8 { 9 int jg=0,jk=getchar()-'0'; 10 while(jk<0||jk>9) jk=getchar()-'0'; 11 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 12 return jg; 13 } 14 const int sj=210; 15 int ca,c,n,m,ans1[sj],bi[sj],a[sj][sj],h[sj][sj<<1],e[sj],dep[sj<<1],s,t,si[sj],ans2[sj],le,ri,mid; 16 bool op; 17 queue<int> q; 18 struct B 19 { 20 int ne,v,w; 21 }b[sj][sj*100]; 22 inline int minn(int x,int y) 23 { 24 return x<y?x:y; 25 } 26 void add(int x,int y,int z,int o) 27 { 28 b[o][e[o]].v=y,b[o][e[o]].w=z,b[o][e[o]].ne=h[o][x],h[o][x]=e[o]++; 29 b[o][e[o]].v=x,b[o][e[o]].w=0,b[o][e[o]].ne=h[o][y],h[o][y]=e[o]++; 30 } 31 void init() 32 { 33 memset(h[0],-1,sizeof(h[0])); 34 n=read(),m=read(); 35 for(int i=1;i<=m;i++) bi[i]=read(); 36 for(int i=1;i<=n;i++) 37 for(int j=1;j<=m;j++) 38 a[i][j]=read(); 39 for(int i=1;i<=n;i++) si[i]=read(); 40 } 41 bool bfs(int x,int o) 42 { 43 memset(dep,0,sizeof(dep)); 44 while(!q.empty()) q.pop(); 45 q.push(x),dep[x]=1; 46 while(!q.empty()) 47 { 48 x=q.front(),q.pop(); 49 for(int i=h[o][x];i!=-1;i=b[o][i].ne) 50 if(b[o][i].w&&!dep[b[o][i].v]) 51 { 52 dep[b[o][i].v]=dep[x]+1; 53 if(b[o][i].v==t) return 1; 54 q.push(b[o][i].v); 55 } 56 } 57 return 0; 58 } 59 int dfs(int x,int f,int o) 60 { 61 if(x==t) return f; 62 int ret=0,d; 63 for(int i=h[o][x];i!=-1;i=b[o][i].ne) 64 if(b[o][i].w&&dep[b[o][i].v]==dep[x]+1) 65 { 66 d=dfs(b[o][i].v,minn(f,b[o][i].w),o); 67 ret+=d,f-=d; 68 b[o][i].w-=d,b[o][i^1].w+=d; 69 } 70 if(!ret) dep[x]=-1; 71 return ret; 72 } 73 inline int dinic(int x) 74 { 75 int ret=0; 76 while(bfs(s,x)) ret+=dfs(s,inf,x); 77 return ret; 78 } 79 void work() 80 { 81 e[0]=0,t=n+m+1; 82 for(int i=1;i<=n;i++) add(s,i,1,0); 83 for(int i=1;i<=m;i++) add(i+n,t,bi[i],0); 84 for(int i=1;i<=n;i++) 85 { 86 op=0; 87 for(int j=1;j<=m+1;j++) 88 { 89 if(!op) 90 { 91 for(int k=0;k<=t;k++) h[i][k]=h[i-1][k]; 92 for(int k=0;k<e[i-1];k++) b[i][k]=b[i-1][k]; 93 e[i]=e[i-1],op=1; 94 } 95 if(j==m+1){ ans1[i]=m+1;break; } 96 for(int k=1;k<=m;k++) 97 if(a[i][k]==j) 98 add(i,k+n,1,i),op=0; 99 if(!op&&dinic(i)){ ans1[i]=j;break; } 100 } 101 } 102 for(int i=1;i<n;i++) printf("%d ",ans1[i]); 103 printf("%d\n",ans1[n]); 104 for(int i=1;i<=n;i++) 105 { 106 if(ans1[i]<=si[i]){ ans2[i]=0;continue; } 107 le=1,ri=i; 108 while(le<ri) 109 { 110 mid=le+ri>>1; 111 for(int k=0;k<=t;k++) h[n+1][k]=h[i-mid-1][k]; 112 for(int k=0;k<e[i-mid-1];k++) b[n+1][k]=b[i-mid-1][k]; 113 e[n+1]=e[i-mid-1]; 114 for(int j=1;j<=m;j++) 115 if(a[i][j]&&a[i][j]<=si[i]) 116 add(i,j+n,1,n+1); 117 if(mid==i||dinic(n+1)) ri=mid; 118 else le=mid+1; 119 } 120 ans2[i]=ri; 121 } 122 for(int i=1;i<n;i++) printf("%d ",ans2[i]); 123 printf("%d\n",ans2[n]); 124 } 125 int main() 126 { 127 ca=read(),c=read(); 128 for(int l=1;l<=ca;l++) 129 { 130 init(); 131 work(); 132 } 133 return 0; 134 }
T2《林克卡特树》
得分:10
只会写直径暴力,想到了问题的实质是找$k+1$条不相交的树上路径但没有时间设计算法了。60分算法用$f[i][j][0/1/2]$表示以$i$为根的子树选了$j$条链,$i$的度数为0/1/2的方案数,作一个合并类型的树归即可。
正解是wqs二分,给每条链加上一个可正可负的权值(buff?),容易想到权值越小选的链越少、权值越大选的链越多。二分这个权值,按照上面的DP方法求最优解,同步维护它所选的链数,根据当前选的链的数量判断所加权值应该向哪边调。感觉思路非常妙,理解了之后也非常自然;出题人是从凸包斜率的角度解释的,也很清楚。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #define ll long long 6 using namespace std; 7 const int sj=300010; 8 int n,k,a1,a2,a3,h[sj],e,sn[sj],tp,fa[sj]; 9 ll f[sj],ans,sum; 10 bool vi[sj]; 11 struct st 12 { 13 int pos; 14 ll vl; 15 friend bool operator < (const st&x,const st&y){ return x.vl<y.vl; } 16 }; 17 priority_queue<st> q; 18 struct B 19 { 20 int ne,v,w; 21 }b[sj<<1]; 22 void add(int x,int y,int z) 23 { 24 b[e].v=y,b[e].w=z,b[e].ne=h[x],h[x]=e++; 25 b[e].v=x,b[e].w=z,b[e].ne=h[y],h[y]=e++; 26 } 27 inline int read() 28 { 29 int jg=0,jk=getchar()-'0',f=1; 30 while(jk<0||jk>9) 31 { 32 if(jk=='-'-'0') f=-f; 33 jk=getchar()-'0'; 34 } 35 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 36 return jg*f; 37 } 38 inline ll maxx(ll x,ll y) 39 { 40 return x>y?x:y; 41 } 42 void dfs(int x) 43 { 44 ll mx=0,nt; 45 for(int i=h[x];i!=-1;i=b[i].ne) 46 if(b[i].v!=fa[x]) 47 { 48 fa[b[i].v]=x,dfs(b[i].v); 49 nt=f[b[i].v]+b[i].w; 50 if(nt>f[x]) mx=f[x],f[x]=nt,sn[x]=b[i].v; 51 else if(nt>mx) mx=nt; 52 } 53 q.push((st){x,f[x]}); 54 ans=maxx(ans,f[x]); 55 ans=maxx(ans,f[x]+mx); 56 } 57 void chang(int x) 58 { 59 if(!x||!vi[sn[x]]) return; 60 f[x]=sn[x]=0; 61 ll nt; 62 for(int i=h[x];i!=-1;i=b[i].ne) 63 if(!vi[b[i].v]) 64 { 65 nt=f[b[i].v]+b[i].w; 66 if(nt>f[x]) f[x]=nt,sn[x]=b[i].v; 67 } 68 chang(fa[x]); 69 } 70 void work() 71 { 72 sum=q.top().vl,tp=q.top().pos; 73 while(tp) vi[tp]=1,tp=sn[tp]; 74 chang(q.top().pos),q.pop(); 75 for(int i=1;i<=k;i++) 76 { 77 while(vi[q.top().pos]) q.pop(); 78 while(q.top().vl!=f[q.top().pos]) q.pop(); 79 sum+=q.top().vl,tp=q.top().pos; 80 while(tp) vi[tp]=1,tp=sn[tp]; 81 chang(q.top().pos),q.pop(); 82 } 83 ans=maxx(ans,sum); 84 } 85 int main() 86 { 87 //freopen("lct1.in","r",stdin); 88 freopen("lct.in","r",stdin); 89 freopen("lct.out","w",stdout); 90 n=read(),k=read(); 91 memset(h,-1,sizeof(h)); 92 for(int i=1;i<n;i++) 93 { 94 a1=read(),a2=read(),a3=read(); 95 add(a1,a2,a3); 96 } 97 dfs(1); 98 if(k!=0) work(); 99 printf("%lld",ans); 100 fclose(stdin);fclose(stdout); 101 return 0; 102 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int sj=300010; 7 int n,k,e,h[sj],a1,a2,a3,size[sj]; 8 ll f[sj][110][3],ans,tmp[110][3]; 9 struct B 10 { 11 int ne,v,w; 12 }b[sj<<1]; 13 void add(int x,int y,int z) 14 { 15 b[e].v=y,b[e].w=z,b[e].ne=h[x],h[x]=e++; 16 b[e].v=x,b[e].w=z,b[e].ne=h[y],h[y]=e++; 17 } 18 inline void maxx(ll &x,ll y) 19 { 20 x=x>y?x:y; 21 } 22 inline int minn(int x,int y) 23 { 24 return x<y?x:y; 25 } 26 inline int read() 27 { 28 int jg=0,jk=getchar()-'0',f=1; 29 while(jk<0||jk>9) 30 { 31 if(jk=='-'-'0') f=-f; 32 jk=getchar()-'0'; 33 } 34 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 35 return jg*f; 36 } 37 void dp(int x,int fx) 38 { 39 size[x]=1,f[x][0][0]=0; 40 for(int i=h[x];i!=-1;i=b[i].ne) 41 if(b[i].v!=fx) 42 { 43 dp(b[i].v,x); 44 memcpy(tmp,f[x],sizeof(tmp)); 45 for(int j=0;j<=minn(k,size[x]);++j) 46 for(int l=0;l<=minn(k,size[b[i].v])&&l+j-1<=k;++l) 47 { 48 maxx(tmp[j+l][0],f[b[i].v][l][0]+f[x][j][0]); 49 maxx(tmp[j+l][0],f[b[i].v][l][1]+f[x][j][0]); 50 maxx(tmp[j+l][0],f[b[i].v][l][2]+f[x][j][0]); 51 maxx(tmp[j+l][1],f[b[i].v][l][0]+f[x][j][1]); 52 maxx(tmp[j+l][1],f[b[i].v][l][1]+f[x][j][1]); 53 maxx(tmp[j+l][1],f[b[i].v][l][2]+f[x][j][1]); 54 maxx(tmp[j+l][1],f[b[i].v][l][1]+f[x][j][0]+b[i].w); 55 maxx(tmp[j+l+1][1],f[b[i].v][l][0]+f[x][j][0]+b[i].w); 56 maxx(tmp[j+l][2],f[b[i].v][l][0]+f[x][j][2]); 57 maxx(tmp[j+l][2],f[b[i].v][l][1]+f[x][j][2]); 58 maxx(tmp[j+l][2],f[b[i].v][l][2]+f[x][j][2]); 59 maxx(tmp[j+l][2],f[b[i].v][l][0]+f[x][j][1]+b[i].w); 60 if(j&&l) maxx(tmp[j+l-1][2],f[b[i].v][l][1]+f[x][j][1]+b[i].w); 61 } 62 memcpy(f[x],tmp,sizeof(tmp)); 63 size[x]+=size[b[i].v]; 64 } 65 } 66 int main() 67 { 68 n=read(),k=read()+1; 69 memset(h,-1,sizeof(h)); 70 memset(f,-0x3f,sizeof(f)); 71 for(int i=1;i<n;i++) 72 { 73 a1=read(),a2=read(),a3=read(); 74 add(a1,a2,a3); 75 } 76 dp(1,0); 77 maxx(ans,f[1][k][0]); 78 maxx(ans,f[1][k][1]); 79 maxx(ans,f[1][k][2]); 80 printf("%lld",ans); 81 return 0; 82 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define ll long long 5 #define inf 1e12 6 using namespace std; 7 const int sj=300010; 8 int n,k,e,h[sj],a1,a2,a3,nt,g[sj][3],qwq[3]; 9 ll f[sj][3],ans,tmp[3],le,ri,mid,res,og; 10 struct B 11 { 12 int ne,v,w; 13 }b[sj<<1]; 14 void add(int x,int y,int z) 15 { 16 b[e].v=y,b[e].w=z,b[e].ne=h[x],h[x]=e++; 17 b[e].v=x,b[e].w=z,b[e].ne=h[y],h[y]=e++; 18 } 19 inline int read() 20 { 21 int jg=0,jk=getchar()-'0',f=1; 22 while(jk<0||jk>9) 23 { 24 if(jk=='-'-'0') f=-f; 25 jk=getchar()-'0'; 26 } 27 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 28 return jg*f; 29 } 30 inline void update(ll &x,ll y,int &z,int q){ if(x<y||(x==y)&&z>q) x=y,z=q; } 31 void dp(int x,int fx) 32 { 33 f[x][0]=0; 34 for(int i=h[x];i!=-1;i=b[i].ne) 35 if(b[i].v!=fx) 36 { 37 dp(b[i].v,x); 38 memcpy(tmp,f[x],sizeof(tmp)),memcpy(qwq,g[x],sizeof(qwq)); 39 for(int j=0;j<=2;j++) 40 for(int l=0;l<=2;l++) 41 update(tmp[l],f[b[i].v][j]+f[x][l],qwq[l],g[b[i].v][j]+g[x][l]); 42 update(tmp[1],f[b[i].v][1]+f[x][0]+b[i].w,qwq[1],g[b[i].v][1]+g[x][0]); 43 update(tmp[2],f[b[i].v][0]+f[x][1]+b[i].w,qwq[2],g[b[i].v][0]+g[x][1]); 44 update(tmp[1],f[b[i].v][0]+f[x][0]+b[i].w-mid,qwq[1],g[b[i].v][0]+g[x][0]+1); 45 update(tmp[2],f[b[i].v][1]+f[x][1]+b[i].w+mid,qwq[2],g[b[i].v][1]+g[x][1]-1); 46 memcpy(f[x],tmp,sizeof(tmp)),memcpy(g[x],qwq,sizeof(qwq)); 47 } 48 update(f[x][1],f[x][0]-mid,g[x][1],g[x][0]+1); 49 } 50 int main() 51 { 52 n=read(),k=read()+1; 53 memset(h,-1,sizeof(h)); 54 for(int i=1;i<n;i++) 55 { 56 a1=read(),a2=read(),a3=read(); 57 add(a1,a2,a3); 58 } 59 le=-inf,ri=inf; 60 while(le<ri) 61 { 62 mid=(le+ri)>>1; 63 memset(f,-0x3f,sizeof(f)); 64 memset(g,0,sizeof(g)); 65 dp(1,0); 66 ans=f[1][0],nt=g[1][0]; 67 update(ans,f[1][1],nt,g[1][1]); 68 update(ans,f[1][2],nt,g[1][2]); 69 if(nt<=k) ri=mid,res=ans+k*mid; 70 else le=mid+1; 71 } 72 printf("%lld",res); 73 return 0; 74 }
T3《制胡窜》
得分:20
考场上写了15分$qn^2$的暴力和5分的全0,后一部分调了很久很久。正解SAM+线段树合并,还需要一点时间理解。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define ll long long 5 #define ull unsigned long long 6 using namespace std; 7 inline int read() 8 { 9 int jg=0,jk=getchar()-'0'; 10 while(jk<0||jk>9) jk=getchar()-'0'; 11 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 12 return jg; 13 } 14 const ull p=1e9+7; 15 const int sj=100010; 16 int n,q,le,ri,sum[sj],len; 17 ll ans,a1,a2; 18 bool op; 19 char s[sj]; 20 ull hs[sj],pf[sj],nt; 21 void work2() 22 { 23 for(int i=1;i<=q;i++) 24 { 25 le=read(),ri=read(),len=ri-le+1; 26 nt=hs[ri]-hs[le-1]*pf[len],ans=0; 27 for(int j=1;j<=2*n;j++) 28 { 29 sum[j]=sum[j-1]; 30 if(j<=n&&j>=len&&nt==hs[j]-hs[j-len]*pf[len]) sum[j]++; 31 } 32 for(int j=1;j<=n;j++) 33 for(int k=j+2;k<=n;k++) 34 if(sum[j]>0||sum[n]-sum[k+len-2]>0||sum[k-1]-sum[j+len-1]>0) 35 ans++; 36 printf("%lld\n",ans); 37 } 38 } 39 inline ll solve(int x) 40 { 41 ll ret=(ll)(n-x)*(n-x-1)/2; 42 ret+=(ll)(n-x)*(n-x-1); 43 return ret; 44 } 45 void work1() 46 { 47 for(int i=1;i<=q;i++) 48 { 49 le=read(),ri=read(),len=ri-le+1; 50 if(len<=(n-1)/3+1) ans=(ll)(n-2)*(n-1)/2; 51 else 52 { 53 a1=n-len+1,a2=len; 54 ans=solve(len); 55 if(a1>a2) ans-=solve(n-(a1-a2)); 56 } 57 printf("%lld\n",ans); 58 } 59 } 60 int main() 61 { 62 //freopen("cutting1.in","r",stdin); 63 //freopen("me.out","w",stdout); 64 freopen("cutting.in","r",stdin); 65 freopen("cutting.out","w",stdout); 66 n=read(),q=read(); 67 scanf("%s",s+1); 68 pf[0]=1,op=1; 69 for(int i=1;i<=n;i++) 70 { 71 hs[i]=(hs[i-1]+(ull)(s[i]-'0'+1))*p; 72 pf[i]=pf[i-1]*p; 73 if(s[i]-'0'!=0) op=0; 74 } 75 if(op) work1(); 76 else work2(); 77 fclose(stdin);fclose(stdout); 78 return 0; 79 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #define ll long long 6 using namespace std; 7 const int ss=200010; 8 int n,q,le,ri,pos[ss],step[ss],pre[ss],last,tot,ch[ss][10],h[ss],e,rt[ss],cnt; 9 int p[ss][20],bin[20],dep[ss],tp; 10 ll res; 11 char s[ss]; 12 vector<int> nt[ss]; 13 vector<int>::iterator it; 14 struct B 15 { 16 int ne,v; 17 }b[ss],ask1,ask2; 18 struct BL 19 { 20 ll ne,v; 21 }ask3; 22 struct que 23 { 24 int len;ll ans; 25 }qu[ss*3/2]; 26 struct tree 27 { 28 int lc,rc,sum1,mi,mx,size; 29 ll sum2; 30 }t[ss*20]; 31 inline void pushup(int x) 32 { 33 t[x].size=t[t[x].lc].size+t[t[x].rc].size; 34 if(t[x].rc) t[x].mx=t[t[x].rc].mx; 35 else t[x].mx=t[t[x].lc].mx; 36 if(t[x].lc) t[x].mi=t[t[x].lc].mi; 37 else t[x].mi=t[t[x].rc].mi; 38 t[x].sum1=t[x].mx-t[x].mi; 39 t[x].sum2=t[t[x].lc].sum2+t[t[x].rc].sum2; 40 if(t[x].lc&&t[x].rc) 41 t[x].sum2+=(ll)t[t[x].rc].mi*(t[t[x].rc].mi-t[t[x].lc].mx); 42 } 43 inline void add(int x,int y) 44 { 45 b[e].v=y,b[e].ne=h[x],h[x]=e++; 46 } 47 void insert(int x) 48 { 49 int np=++tot,p=last; 50 last=np,step[np]=step[p]+1; 51 while(p&&!ch[p][x]) ch[p][x]=np,p=pre[p]; 52 if(!p) pre[np]=1; 53 else 54 { 55 int q=ch[p][x]; 56 if(step[q]==step[p]+1) pre[np]=q; 57 else 58 { 59 int nq=++tot;step[nq]=step[p]+1; 60 memcpy(ch[nq],ch[q],sizeof(ch[q])); 61 pre[nq]=pre[q],pre[np]=pre[q]=nq; 62 while(ch[p][x]==q) ch[p][x]=nq,p=pre[p]; 63 } 64 } 65 } 66 inline int read() 67 { 68 int jg=0,jk=getchar()-'0'; 69 while(jk<0||jk>9) jk=getchar()-'0'; 70 while(jk>=0&&jk<=9) jg*=10,jg+=jk,jk=getchar()-'0'; 71 return jg; 72 } 73 inline ll calc(ll x,ll y) 74 { 75 return (x+y)*(y-x+1)/2; 76 } 77 void update(int &x,int l,int r,int pos) 78 { 79 if(!x) x=++cnt; 80 t[x].size=1,t[x].mx=t[x].mi=pos; 81 if(l==r) return; 82 int mid=l+r>>1; 83 if(pos<=mid) update(t[x].lc,l,mid,pos); 84 else update(t[x].rc,mid+1,r,pos); 85 } 86 void dfs(int x) 87 { 88 p[x][0]=pre[x],dep[x]=dep[pre[x]]+1; 89 for(int j=1;bin[j]<=dep[x];j++) 90 p[x][j]=p[p[x][j-1]][j-1]; 91 for(int i=h[x];i!=-1;i=b[i].ne) 92 dfs(b[i].v); 93 } 94 void merge(int &x,int y) 95 { 96 if(!x){ x=y;return; } 97 if(!y) return; 98 merge(t[x].lc,t[y].lc),merge(t[x].rc,t[y].rc); 99 pushup(x); 100 } 101 void getpre(int x,int l,int r,int v) 102 { 103 if(l==r) 104 { 105 ask1.ne=l,ask1.v++; 106 return; 107 } 108 int mid=l+r>>1; 109 if(t[x].rc&&t[t[x].rc].mi<v) ask1.v+=t[t[x].lc].size,getpre(t[x].rc,mid+1,r,v); 110 else getpre(t[x].lc,l,mid,v); 111 } 112 void getnxt(int x,int l,int r,int v) 113 { 114 if(l==r) 115 { 116 ask2.ne=l,ask2.v--; 117 return; 118 } 119 int mid=l+r>>1; 120 if(t[x].lc&&t[t[x].lc].mx>v) ask2.v-=t[t[x].rc].size,getnxt(t[x].lc,l,mid,v); 121 else getnxt(t[x].rc,mid+1,r,v); 122 } 123 int getkth(int x,int l,int r,int v) 124 { 125 if(l==r) return l; 126 int mid=l+r>>1; 127 if(t[t[x].lc].size>=v) return getkth(t[x].lc,l,mid,v); 128 return getkth(t[x].rc,mid+1,r,v-t[t[x].lc].size); 129 } 130 void query(int x,int l,int r,int z,int y) 131 { 132 if(z<=l&&r<=y) 133 { 134 ask3.ne+=t[x].sum1,ask3.v+=t[x].sum2; 135 return; 136 } 137 int mid=l+r>>1; 138 if(z<=mid) query(t[x].lc,l,mid,z,y); 139 if(y>mid) query(t[x].rc,mid+1,r,z,y); 140 if(z<=mid&&y>mid&&t[x].lc&&t[x].rc) 141 { 142 int qwq=t[t[x].rc].mi-t[t[x].lc].mx; 143 ask3.ne+=qwq,ask3.v+=(ll)qwq*t[t[x].rc].mi; 144 } 145 } 146 void solve(int x) 147 { 148 for(int i=h[x];i!=-1;i=b[i].ne) 149 solve(b[i].v),merge(rt[x],rt[b[i].v]); 150 int r1=t[rt[x]].mi,lx; 151 for(it=nt[x].begin();it!=nt[x].end();it++) 152 { 153 tp=*it,le=qu[tp].len,ri=t[rt[x]].mx-le+1; 154 if(t[rt[x]].size==1) 155 { qu[tp].ans-=(ll)(le-1)*(ri-1)+calc(n-r1,n-(ri+1));continue; } 156 ask1.v=0,ask2.v=t[rt[x]].size+1; 157 getpre(rt[x],1,n,r1+le-1),getnxt(rt[x],1,n,ri); 158 lx=ask1.ne-le+1; 159 if(ask1.v+1<ask2.v) continue; 160 if(ask1.v+1==ask2.v) 161 { qu[tp].ans-=(ll)(r1-lx)*(ask2.ne-ri);continue; } 162 if(ask2.v==1) 163 { 164 qu[tp].ans-=(ll)(r1-le)*(r1-lx)+calc(n-r1,n-lx-1); 165 qu[tp].ans-=t[rt[x]].sum2-(ll)t[rt[x]].sum1*ri; 166 } 167 else 168 { 169 qu[tp].ans-=(ll)(r1-lx)*(getkth(rt[x],1,n,ask1.v+1)-ri); 170 if(ask2.v<=ask1.v) 171 { 172 ask3.ne=ask3.v=0; 173 query(rt[x],1,n,getkth(rt[x],1,n,ask2.v-1),ask1.ne); 174 qu[tp].ans-=ask3.v-ask3.ne*ri; 175 } 176 } 177 } 178 } 179 inline int find(int x,int ln) 180 { 181 int ret=pos[x]; 182 for(int j=18;j>=0;j--) 183 if(step[p[ret][j]]>=ln) 184 ret=p[ret][j]; 185 return ret; 186 } 187 int main() 188 { 189 memset(h,-1,sizeof(h)); 190 bin[0]=1; 191 for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1; 192 n=read(),q=read(); 193 scanf("%s",s+1); 194 last=tot=1,res=(ll)(n-1)*(n-2)/2; 195 for(int i=1;i<=n;i++) 196 { 197 insert(s[i]-'0'),pos[i]=last; 198 update(rt[last],1,n,i); 199 } 200 for(int i=2;i<=tot;i++) add(pre[i],i); 201 dfs(1); 202 for(int i=1;i<=q;i++) 203 { 204 le=read(),ri=read(); 205 qu[i].len=ri-le+1,qu[i].ans=res; 206 nt[find(ri,qu[i].len)].push_back(i); 207 } 208 solve(1); 209 for(int i=1;i<=q;i++) printf("%lld\n",qu[i].ans); 210 return 0; 211 }