「九省联考 2018」制胡窜 解题报告

「九省联考 2018」制胡窜

苟题目,搞了我一天。

显然要搞一个SAM,然后搞一个线段树合并,关于定位询问串搞一个树上倍增

然后你考虑一个细节贼多的分类讨论

应该是可以不求补集的,我最开始一直这么想但是有个东西不会维护后来发现是可以维护的...

但是补集应该简单一点吧...?

就是每次切两刀要把所有位置的刀切开,这么多细节我肯定懒得说。

说下我错过的(如果你写法和我类似)

注意左边的到切的是[,),右边的切的是(,]

然后就是讨论讨论讨论


Code:

#include 
#include 
#include 
#include 
#define ll long long
using std::min;
using std::max;
const int N=2e5+10;
template 
void read(T &x)
{
    x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int root[N];
namespace seg
{
    int ch[N*50][2],yuul[N*50],yuur[N*50],tot;
    ll s1[N*50],s2[N*50];
    #define ls ch[now][0]
    #define rs ch[now][1]
    void updata(int now)
    {
        yuul[now]=yuul[ls]?yuul[ls]:yuul[rs];
        yuur[now]=yuur[rs]?yuur[rs]:yuur[ls];
        s1[now]=s1[ls]+s1[rs];
        s2[now]=s2[ls]+s2[rs];
        int r1=yuur[ls],r2=yuul[rs];
        if(r1&&r2)
        {
            s1[now]+=1ll*(r2-r1)*r2;
            s2[now]+=(r2-r1);
        }
    }
    int qryex(int now,int L,int R,int l,int r)//有没有点..
    {
        if(!now) return 0;
        if(l==L&&r==R) return 1;
        int Mid=L+R>>1;
        if(r<=Mid) return qryex(ls,L,Mid,l,r);
        else if(l>Mid) return qryex(rs,Mid+1,R,l,r);
        else return qryex(ls,L,Mid,l,Mid)||qryex(rs,Mid+1,R,Mid+1,r);
    }
    void qrys(int now,int L,int R,int l,int r,ll &ret1,ll &ret2)
    {
        if(!now) return;
        if(l==L&&r==R)
        {
            ret1+=s1[now],ret2+=s2[now];
            return;
        }
        int Mid=L+R>>1;
        if(r<=Mid) qrys(ls,L,Mid,l,r,ret1,ret2);
        else if(l>Mid) qrys(rs,Mid+1,R,l,r,ret1,ret2);
        else
        {
            qrys(ls,L,Mid,l,Mid,ret1,ret2),qrys(rs,Mid+1,R,Mid+1,r,ret1,ret2);
            int r1=yuur[ls],r2=yuul[rs];
            if(r1&&r2&&r1>=l&&r2<=r)
            {
                ret1+=1ll*(r2-r1)*r2;
                ret2+=(r2-r1);
            }
        }
    }
    //qryri,小于等于某位置最右
    //qryle,大于等于某位置最左
    int qryri(int now,int l,int r,int p)
    {
        if(r==p) return yuur[now];
        int mid=l+r>>1;
        if(p<=mid) return qryri(ls,l,mid,p);
        else return max(yuur[ls],qryri(rs,mid+1,r,p));
    }
    int qryle(int now,int l,int r,int p)
    {
        if(l==p) return yuul[now];
        int mid=l+r>>1;
        if(p<=mid)
        {
            int ret=qryle(ls,l,mid,p);
            if(ret) return ret;
            else return yuul[rs];
        }
        else return qryle(rs,mid+1,r,p);
    }
    void ins(int &now,int l,int r,int p)
    {
        if(!now) now=++tot;
        if(l==r)
        {
            yuul[now]=yuur[now]=l;
            return;
        }
        int mid=l+r>>1;
        if(p<=mid) ins(ls,l,mid,p);
        else ins(rs,mid+1,r,p);
        updata(now);
    }
    int Merge(int x,int y,int l,int r)
    {
        if(!x||!y) return x^y;
        if(l==r) return x;
        int mid=l+r>>1,now=++tot;
        ls=Merge(ch[x][0],ch[y][0],l,mid);
        rs=Merge(ch[x][1],ch[y][1],mid+1,r);
        updata(now);
        return now;
    }
}
ll gets(int n){return 1ll*n*(n+1)/2;}
using seg::ins;
using seg::Merge;
using seg::qryex;
using seg::qryri;
using seg::qryle;
using seg::qrys;
int n,q;
char s[N];
int ch[N][10],len[N],par[N],f[19][N],pos[N],tax[N],A[N],tot=1,las=1;
void extend(int c,int pos)
{
    int now=++tot,p=las;
    ins(root[now],1,n,pos);
    len[now]=len[p]+1;
    while(p&&!ch[p][c]) ch[p][c]=now,p=par[p];
    if(!p) par[now]=1;
    else
    {
        int x=ch[p][c];
        if(len[x]==len[p]+1) par[now]=x;
        else
        {
            int y=++tot;
            len[y]=len[p]+1;
            par[y]=par[x];
            memcpy(ch[y],ch[x],sizeof ch[y]);
            while(p&&ch[p][c]==x) ch[p][c]=y,p=par[p];
            par[now]=par[x]=y;
        }
    }
    las=now;
}
int clim(int now,int d)
{
    for(int i=18;~i;i--)
        if(len[f[i][now]]>=d)
            now=f[i][now];
    return now;
}
//qryri,小于等于某位置最右
//qryle,大于等于某位置最左
ll work(int now,int d)
{
    if(d==1) return 0;
    int lp=qryri(root[now],1,n,n)-d+1;//最右边点的左端点
    int rp=qryle(root[now],1,n,1);//最左边点的右端点
    if(qryex(root[now],1,n,rp+d,lp-1)) return 0;//有3个互不香蕉
    ll ret=0;
    if(rp<=lp)
    {
        int s=qryle(root[now],1,n,lp);//开始的右端点位置
        int t=qryri(root[now],1,n,rp+d-1);//结束的右端点位置
        if(s>t) return (rp-(t-d+1))*(s-lp);
        int nxts=s;//当前的右端点
        s=qryri(root[now],1,n,s-1);//前一个右端点
        if(s) ret+=1ll*(nxts-s)*(nxts-lp);
        ll ret1=0,ret2=0;
        qrys(root[now],1,n,nxts,t,ret1,ret2);
        ret+=ret1-ret2*lp;
        int nxtt=qryle(root[now],1,n,t+1);//后一个右端点
        ret+=1ll*(rp-(t-d+1))*(nxtt-lp);
        return ret;
    }
    ret+=1ll*(rp-d)*(rp-lp);//i不切,j全切
    ret+=1ll*(rp-lp)*(n-1)-(gets(rp-1)-gets(lp-1));
    int s=rp,t=lp+d-1;
    ll ret1=0,ret2=0;
    qrys(root[now],1,n,s,t,ret1,ret2);
    ret+=ret1-ret2*lp;
    //int nxts=qryle(root[now],1,n,s+1);
    //ret+=(nxts-rp)*(nxts-lp+1);
    return ret;
}
int main()
{
    read(n),read(q);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) extend(s[i]-'0',i),pos[i]=las;
    for(int i=1;i<=tot;i++) ++tax[len[i]];
    for(int i=1;i<=n;i++) tax[i]+=tax[i-1];
    for(int i=1;i<=tot;i++) A[tax[len[i]]--]=i;
    for(int i=tot;i;i--)
    {
        int now=A[i],p=par[now];
        f[0][now]=p;
        root[p]=Merge(root[p],root[now],1,n);
    }
    for(int i=1;i<=tot;i++)
    {
        int now=A[i];
        for(int j=1;f[j-1][now];j++) f[j][now]=f[j-1][f[j-1][now]];
    }
    ll ans=1ll*(n-1)*(n-2)/2;
    for(int l,r,d,i=1;i<=q;i++)
    {
        read(l),read(r),d=r+1-l;
        int now=clim(pos[r],d);
        printf("%lld\n",ans-work(now,d));
    }
    return 0;
}

2019.3.19

转载于:https://www.cnblogs.com/butterflydew/p/10561892.html

你可能感兴趣的:(「九省联考 2018」制胡窜 解题报告)