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 }
chess-handon
 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 }
chess-100pts

 

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 }
iiidx-handon
  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 }
iiidx-100pts

 

 

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 }
coat-handon
 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 }
coat-100pts

 

 

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 }
mentor-handon
 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 }
mentor-60pts
  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 }
mentor-100pts

 

 

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 }
lct-handon
 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 }
lct-60pts
 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 }
lct-100pts

 

 

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 }
cutting-handon
  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 }
cutting-100pts

 

posted @ 2018-04-11 12:07  moyiii  阅读(435)  评论(0编辑  收藏  举报