bzoj5253 [2018多省省队联测]制胡窜(SAM+线段树合并+树上倍增+数学)

这题细节好多阿orz好毒瘤阿orz

首先我们对于每一个询问子串,如果处理出了他的所有出现位置,那么我们反过来求,就是要求用两个断点把所有线段都断开的方案数。

处理出所有询问子串的出现位置可以SAM+parent树上倍增来快速定位子串所在节点,这个节点的Right集合就是所有的出现位置。我们可以用线段树合并来维护这个Right集合。复杂度 O(Q+nlogn) O ( ( Q + n ) l o g n )

然后我们考虑用两个断点把所有线段都断开的方案数怎么求。
首先如果存在三条及以上互不相交的线段则肯定无解。
那么至多只有第一条和最后一条线段不相交。
我们考虑第一个断点放在li~li+1之间时,第二个断点必须放在ln~ri+1之间。
所以总方案数应该是 i(li+1li)(ri+1ln) ∑ i ( l i + 1 − l i ) ∗ ( r i + 1 − l n )
因为长度都相等,因此也就是 i(ri+1ri)(ri+1ln) ∑ i ( r i + 1 − r i ) ∗ ( r i + 1 − l n )
也就是 i(ri+1ri)ri+1(ri+1ri)ln) ∑ i ( r i + 1 − r i ) ∗ r i + 1 − ( r i + 1 − r i ) ∗ l n )
我们分别维护一下 (ri+1ri)ri+1 ( r i + 1 − r i ) ∗ r i + 1 (ri+1ri) ( r i + 1 − r i ) 即可。
还要注意很多的细节。。。比如li~li+1一定要和第一条线段有交,ri+1要>ln等等。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100010
#define inf 0x3f3f3f3f
#define pa pair<int,int>
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,Q,m=0,par[N<<1],son[N<<1][10],fa[N<<1][20],Log[N<<1],lst[N],mx[N<<1],owo=0,Rt=0;
int rt[N<<1];char s[N];
ll ans[N*3];
vector<int>Son[N<<1];vector<pa> qu[N<<1];
struct node{
    int lc,rc,mn,mx,sum1,sz;
    ll sum2;
}tr[N*20];
inline void pushup(int p){
    int l=tr[p].lc,r=tr[p].rc;
    tr[p].sz=tr[l].sz+tr[r].sz;
    tr[p].mn= l?tr[l].mn:tr[r].mn;
    tr[p].mx= r?tr[r].mx:tr[l].mx;
    tr[p].sum1=tr[p].mx-tr[p].mn;
    tr[p].sum2=tr[l].sum2+tr[r].sum2;
    if(l&&r) tr[p].sum2+=((ll)tr[r].mn-tr[l].mx)*tr[r].mn;
}
inline void ins(int &p,int l,int r,int x){
    p=++owo;tr[p].mn=tr[p].mx=x;tr[p].sz=1;
    if(l==r) return;int mid=l+r>>1;
    if(x<=mid) ins(tr[p].lc,l,mid,x);
    else ins(tr[p].rc,mid+1,r,x);
}
inline void merge(int &p1,int p2){
    if(!p1){p1=p2;return;}
    if(!p2) return;
    merge(tr[p1].lc,tr[p2].lc);
    merge(tr[p1].rc,tr[p2].rc);pushup(p1);
}
inline void qsucc(int p,int l,int r,int x,int &pos,int &rx){//查x的后继
    if(l==r){rx=l;return;}
    int mid=l+r>>1;
    if(tr[tr[p].lc].mx>x) pos-=tr[tr[p].rc].sz,qsucc(tr[p].lc,l,mid,x,pos,rx);
    else qsucc(tr[p].rc,mid+1,r,x,pos,rx);
}
inline void qpre(int p,int l,int r,int x,int &pos,int &rx){//查x的前驱
    if(l==r){rx=l;return;}
    int mid=l+r>>1;
    if(tr[tr[p].rc].mn<x) pos+=tr[tr[p].lc].sz,qpre(tr[p].rc,mid+1,r,x,pos,rx);
    else qpre(tr[p].lc,l,mid,x,pos,rx);
}
inline int qkth(int p,int l,int r,int x){
    if(l==r) return l;
    int mid=l+r>>1;
    if(x<=tr[tr[p].lc].sz) return qkth(tr[p].lc,l,mid,x);
    return qkth(tr[p].rc,mid+1,r,x-tr[tr[p].lc].sz);
}
inline void query(int p,int l,int r,int x,int y,int &sum1,ll &sum2){
    if(!p) return;
    if(x==l&&r==y){sum1+=tr[p].sum1;sum2+=tr[p].sum2;return;}
    int mid=l+r>>1;
    if(y<=mid) query(tr[p].lc,l,mid,x,y,sum1,sum2);
    else if(x>mid) query(tr[p].rc,mid+1,r,x,y,sum1,sum2);
    else{
        query(tr[p].lc,l,mid,x,mid,sum1,sum2);
        query(tr[p].rc,mid+1,r,mid+1,y,sum1,sum2);
        int pl=tr[p].lc,pr=tr[p].rc;
        if(pl&&pr) sum1+=tr[pr].mn-tr[pl].mx,sum2+=((ll)tr[pr].mn-tr[pl].mx)*tr[pr].mn;
    }
}
inline void extend(int ch,int x){
    int p=lst[x-1],np=++m;mx[np]=mx[p]+1;lst[x]=np;
    ins(rt[np],1,n,x);
    for(;!son[p][ch];p=par[p]) son[p][ch]=np;
    if(!p){par[np]=Rt;return;}
    int q=son[p][ch];
    if(mx[q]==mx[p]+1){par[np]=q;return;}
    int nq=++m;memcpy(son[nq],son[q],sizeof(son[q]));mx[nq]=mx[p]+1;
    par[nq]=par[q];par[q]=par[np]=nq;
    for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline void dfs1(int x){
    for(int i=1;i<=Log[m];++i){
        if(!fa[x][i-1]) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
    }for(int i=0;i<Son[x].size();++i){
        int y=Son[x][i];if(y==fa[x][0]) continue;
        fa[y][0]=x;dfs1(y);
    }
}
inline int getx(int x,int l){
    for(int i=Log[m];i>=0;--i)
        if(mx[fa[x][i]]>=l) x=fa[x][i];
    return x;
}
inline ll S(int l,int r){return ((ll)l+r)*(r-l+1)>>1;}
inline void dfs(int x){
    for(int i=0;i<Son[x].size();++i){
        int y=Son[x][i];if(y==fa[x][0]) continue;dfs(y);
        merge(rt[x],rt[y]);
    }for(int i=0;i<qu[x].size();++i){
        int id=qu[x][i].second,len=qu[x][i].first,r1=tr[rt[x]].mn,ln=tr[rt[x]].mx-len+1;
        if(tr[rt[x]].sz==1){ans[id]=((ll)ln-1)*(len-1)+S(n-r1,n-ln-1);continue;}
        int pos1=tr[rt[x]].sz,pos2=1,rx,ry;qsucc(rt[x],1,n,ln,pos1,rx);qpre(rt[x],1,n,r1+len-1,pos2,ry);
        int lx=rx-len+1,ly=ry-len+1;
        //pos1--第一个和最后一条直线有交的,pos2--最后一个和第一条直线有交的。
        if(pos1>pos2+1) continue;
        if(pos1==pos2+1){ans[id]=((ll)r1-ly)*(rx-ln);continue;}
        if(pos1==1){
            ans[id]=(ll)(r1-len)*(r1-ln)+S(n-r1,n-ln-1);
            ans[id]+=tr[rt[x]].sum2-(ll)tr[rt[x]].sum1*ln;continue;
        }ans[id]=((ll)r1-ly)*(qkth(rt[x],1,n,pos2+1)-ln);
        int sum1=0;ll sum2=0;query(rt[x],1,n,qkth(rt[x],1,n,pos1-1),ry,sum1,sum2);
        ans[id]+=sum2-(ll)sum1*ln;
    }
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();Q=read();Rt=lst[0]=++m;tr[0].mx=-inf;tr[0].mn=inf;
    scanf("%s",s+1);Log[0]=-1;
    for(int i=1;i<=n;++i) extend(s[i]-'0',i);
    for(int i=1;i<=m;++i) Son[par[i]].push_back(i),Log[i]=Log[i>>1]+1;
    dfs1(Rt);for(int i=1;i<=Q;++i){
        int l=read(),r=read(),x=getx(lst[r],r-l+1);
        qu[x].push_back(make_pair(r-l+1,i));
    }dfs(Rt);ll ALL=S(1,n-2);
    for(int i=1;i<=Q;++i) printf("%lld\n",ALL-ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值