「九省联考 2018」制胡窜

题目描述

对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度。
接着,我们定义 \(S_i\) 表示 \(S\) 中第 \(i\) 个字符,\(S_{L,R}\) 表示由 \(S\) 中从左往右数,第 \(L\) 个字符到第 \(R\) 个字符依次连接形成的字符串。特别的,如果 \(L > R\) ,或者 \(L < [1, |S|]\), 或者 \(R < [1, |S|]\) 我们可以认为 \(S_{L,R}\) 为空串。
给定一个长度为 \(n\) 的仅由数字构成的字符串 \(S\),现在有 \(q\) 次询问,第 \(k\) 次询问会给出 \(S\) 的一个字符串 \(S_{l,r}\) ,请你求出有多少对 \((i, j)\),满足 \(1 \le i < j \le n\)\(i + 1 \lt j\),且 \(S_{l,r}\) 出现在 \(S_{1,i}\) 中或 \(S_{i+1, j−1}\) 中或 \(S_{j,n}\) 中。
对于所有测试数据,\(1 \le n \le 10^5\)\(1 \le q \le 3 · 10^5\)\(1 \le l \le r \le n\)

题解

大概思路就是正难则反、分类讨论。
将原问题转化为被分成的3部分都不含盖子串的方案数。
可以将分割看成选原串之间的缝隙切2刀。

Case 1:

有可能第一刀就已经满足条件了,此时要求切到子串最左位置和最右位置。第二刀随便切在右边就行了。

Case 2:

有可能第一刀没有切掉任何一个子串,第二刀切完。此时要求第一刀切在第一个子串左边。

Case 3:

有可能第一刀切了一些,第二刀切了剩下的。设第一刀切了前\(i\)个串,即刚好没有切掉第i+1个串。
\(L_i,R_i\)为子串第\(i\)次出现的左右端点,显然有\(R_i=L_i+Len-1\)\(Len\)为串长。
再令\(P_2=R_m-Len+1,P_1=R_1+len-1\),其中m为子串出现次数。
观察第一刀、第二刀满足的条件,为:\(L_i<R_1,R_{i+1}>L_m\)
化简,得\(R_{i+1}>P_2,R_i<P_1\)
另外显然\(R_{i+1}>R_i\)
于是我们得到了大小关系的几种情况:

\(R_{i}<P_{1}=P_{2}<R_{i+1}\)

\(R_{i}<P_{1}<P_{2}<R_{i+1}\)

\(R_{i}<P_{2}<P_{1}<R_{i+1}\)

$P_{2} \leq R_{i}<R_{i+1} \leq P_{1} $

$P_{2}<R_{i}<P_{1}<R_{i+1} $

\(R_{i}<P_{2}<R_{i+1}<P_{1}\)

\(R_{i}=P_{2}<P_{1}<R_{i+1}\)

\(R_{i}<P_{2}<P_{1}=R_{i+1}\)

除了第4种情况,显然其他情况最多只有一个\(i\)满足条件。

然后考虑每个\(i\)对答案的贡献:\((L_{i+1}-L_{i})*(R_{i+1}-L_{m})\)

但是这个式子过不了样例。。。

观察一下样例的第一个询问,发现第一刀的范围不一定是\((L_{i+1}-L_{i})\),有可能\(R_{1}\)\(L_{i}\)\(L_{i+1}\)

所以式子变为

\(Min(L_{i+1}-L_{i},R_1-L_i)*(R_{i+1}-L_{m})=Min(R_{i+1}-R_{i},P_1-R_i)*(R_{i+1}-L_{m})\)

考虑怎么求值。应该知道要用线段树求出Right集合了吧

除了第4种情况以外,其他情况最多只需要求前驱、后继就行了。

第四种情况发现Min是可以去掉的(虽然我的代码智障地全都加了),于是\(\sum (R_{i+1}-R_{i})*(R_{i+1}-L_{m})=\sum (R_{i+1}-R_{i})*R_{i+1}- \sum L_{m}*(R_{i+1}-R_{i})=\sum (R_{i+1}-R_{i})*R_{i+1}-L_m*(R_y-R_x)\),其中\(x,y\)分别为最前、最后一个满足条件的串。于是我们需要多维护一个\(\sum (R_{i+1}-R_{i})*R_{i+1}\),发现这个东西在存了区间最大最小值后,可以左右区间合并。

然后就没有啦。具体细节(如倍增定位子串位置)见辣鸡代码

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int Q=1<<19,P=1<<20;
#define ll long long
int ch[P][10],mx[P],par[P];
int tot=0;
int Push(int val)
{
    mx[++tot]=val;
    return tot;
}
int Rt=Push(0),lp=Rt;
int sgn[P];
int include_today_GG=0;
void Sam(int v)
{
    int p=lp,np=Push(mx[lp]+1);
    for(;p&&(!ch[p][v]);p=par[p])
        ch[p][v]=np;
    if(!p)par[np]=Rt;
    else{
        int q=ch[p][v];
        if(mx[p]+1==mx[q])par[np]=q;
        else{
            int nq=Push(mx[p]+1);
            for(int i=0;i<10;i++)
                ch[nq][i]=ch[q][i];
            par[nq]=par[q],par[q]=par[np]=nq;
            for(;p&&ch[p][v]==q;p=par[p])
                ch[p][v]=nq;
        }
    }
    sgn[np]=++include_today_GG;
    lp=np;
}
struct graph{
    int inc;
    int las[P],e[P],nn[P];
    void ins(int x,int y)
    {
        e[++inc]=y;
        nn[inc]=las[x];
        las[x]=inc;
    }
}tr,qu;
const int S=1<<20;
int POOL[S],toap=0;
struct dt{
    int mn,mx;
    ll sm;
}w[S];
dt operator+(dt a,dt b)
{return (dt){min(a.mn,b.mn),max(a.mx,b.mx),a.sm+b.sm+((a.mx>=1&&b.mx>=1)?1LL*b.mn*(b.mn-a.mx):0)};}
int ls[S],rs[S];
void Upd(int x)
{w[x]=w[ls[x]]+w[rs[x]];}
int tl=0;
int New()
{
    int id=toap?POOL[toap--]:++tl;
    w[id]=w[0],ls[id]=rs[id]=0;
    return id;
}
void Merge(int &x,int y,int l,int r)
{
    if((!x)||(!y)){
        if(!x)x=y;
        return;
    }
    int mid=(l+r)>>1;
    Merge(ls[x],ls[y],l,mid);
    Merge(rs[x],rs[y],mid+1,r);
    Upd(x);
    POOL[++toap]=y; 
}
void Mdf(int &x,int l,int r,int owo)
{
    if(!x)x=New();
    if(l==r){
        w[x].mn=w[x].mx=owo;
        return;
    }
    int mid=(l+r)>>1;
    if(owo<=mid)Mdf(ls[x],l,mid,owo);
    else Mdf(rs[x],mid+1,r,owo);
    Upd(x);
}
dt Gans(int now,int l,int r,int x,int y)
{
    if((!now)||(x<=l&&y>=r))return w[now];
    int mid=(l+r)>>1;
    if(y<=mid)return Gans(ls[now],l,mid,x,y);
    if(x>mid)return Gans(rs[now],mid+1,r,x,y);
    return Gans(ls[now],l,mid,x,y)+Gans(rs[now],mid+1,r,x,y);
}
int Qian(int now,int l,int r,int x){
    if(w[now].mn>=x||(!now))return w[0].mn;
    if(l==r)return w[now].mn;
    int mid=(l+r)>>1;
    if(w[rs[now]].mn<x)return Qian(rs[now],mid+1,r,x);
    return Qian(ls[now],l,mid,x);
}
int Hou(int now,int l,int r,int x)
{
    if(w[now].mx<=x||(!now))return w[0].mx;
    if(l==r)return w[now].mx;
    int mid=(l+r)>>1;
    if(w[ls[now]].mx>x)return Hou(ls[now],l,mid,x);
    return Hou(rs[now],mid+1,r,x);
}
int n;
ll ans[Q];
int rt[P];
#define Qian_(owo) Qian(rt[x],1,n,owo)
#define Hou_(owo) Hou(rt[x],1,n,owo)
ll C2(int x)
{return 1LL*x*(x-1)/2;}
ll GG(int x,int len)
{
    ll als=0;
    int r1=Hou_(-1),rn=Qian_(n+1);
    int l1=r1-len+1,ln=rn-len+1;
    int p1=r1+len-1;
    int p2=rn-len+1;
    if(p1==p2){
        int t1=Qian_(p1),t2=Hou_(p2);
        if(t1<=n&&t2>=1&&Hou_(t1)==t2)als+=1LL*min(t2-t1,p1-t1)*(t2-ln);
    }
    if(p1<p2){
        int t1=Qian_(p1),t2=Hou_(p2);
        if(t1<=n&&t2>=1&&Hou_(t1)==t2)als+=1LL*min(t2-t1,p1-t1)*(t2-ln);
    }
    if(p2<p1){
        int t1=Qian_(p2),t2=Hou_(p1);
        if(t1<=n&&t2>=1&&Hou_(t1)==t2)als+=1LL*min(t2-t1,p1-t1)*(t2-ln);
    }
    if(p2<p1){
        int t1=Qian_(p2),t2=Hou_(p1),o;
        if(t1<=n&&(o=Hou_(t1))>p2&&o<p1)als+=1LL*min(o-t1,p1-t1)*(o-ln);
        if(t2>=1&&(o=Qian_(t2))<p1&&o>p2)als+=1LL*min(t2-o,p1-o)*(t2-ln);
        if(t1<=n&&t2>=1&&t1==t2){
            if((o=Hou_(t1))>p1)als+=1LL*min(o-t1,p1-t1)*(o-ln);
            if((o=Qian_(t1))<p2)als+=1LL*min(t1-o,p1-o)*(t1-ln);
        }
        int te=Qian_(p1+1);
        if(p2<=te&&te<=n){
            dt ha=Gans(rt[x],1,n,p2,te-1);
            if(ha.mx>=1)als+=1LL*min(te-ha.mx,p1-ha.mx)*(te-ln)+ha.sm-1LL*(ha.mx-ha.mn)*ln;
        }
        int oo;
        if(Hou_(p2-1)==p2){
            if((oo=Hou_(p2))>p1)als+=1LL*min(oo-p2,p1-p2)*(p1-ln);
        }
        if(Qian_(p1+1)==p1){
            if((oo=Qian_(p1))<p2)als+=1LL*min(p1-oo,p1-oo)*(p1-ln);
        }
    }
    if(ln<r1){
        int del=r1-ln;
        als+=1LL*(l1-1)*del;
        als+=C2(del)+1LL*(n-r1)*del;
    }
    return als;
}
int LEN[Q];
void dfs(int x)
{
    rt[x]=0;
    for(int t=tr.las[x];t;t=tr.nn[t]){
        int y=tr.e[t];
        dfs(y);
        Merge(rt[x],rt[y],1,n);
    }
    if(sgn[x])
    Mdf(rt[x],1,n,sgn[x]);
    for(int t=qu.las[x];t;t=qu.nn[t])
        ans[qu.e[t]]=C2(n-1)-GG(x,LEN[qu.e[t]]);
}
char s[1<<17];
int SUM[Q];
int fa[1<<18][20];
int main()
{
    w[0].mn=998244353,w[0].mx=-998244353;
    int q;
    scanf("%d%d",&n,&q); 
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)Sam(s[i]-'0'),SUM[i]=lp;
    for(int i=1;i<=tot;i++)fa[i][0]=par[i];
    for(int j=1;j<=18;j++)
        for(int i=1;i<=tot;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    for(int i=2;i<=tot;i++)
        tr.ins(par[i],i);
    for(int i=1,l,r;i<=q;i++){
        scanf("%d%d",&l,&r);
        int x=SUM[r];
        LEN[i]=r-l+1;
        for(int i=18;i>=0;--i)
            if(mx[fa[x][i]]>=r-l+1)x=fa[x][i];
        qu.ins(x,i);
    }
    dfs(1);
    for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
    return 0;
}

P.S.:感谢chenjingqi大佬帮忙找错

posted @ 2019-02-23 14:56 蒟蒻小果冻 阅读(...) 评论(...)  编辑 收藏