后缀自动机挺好毒瘤的题。

我们考虑哪些切点是不合法的。肯定是所有的匹配串都被切了。

我们考虑第一个切口的位置。

当第一个切口在第一个出现位置前时,第二个切口必须切掉所有的串。

当第一个切口在$l_{i}$和$l_{i+1}$间的时候(此时必须保证切掉第一个串),第二个切口必须切掉$s_{i+1}$到$s_{cnt}$这些串

当第一个切口在$l_{cnt}$后时(此时依旧需要保证切掉第一个串),第二个切口随便放。

于是我们将询问离线,对于每个询问通过在parent树上倍增来找到所对应的节点。

对于后缀自动机上每个节点,通过平衡树启发式合并来维护他的right集合,之后我们只需要维护$\sum{(l_{i+1}-l_{i}) \cdot (r_{i+1}-l_{cnt})}$即可,然后拆开式子,就是$\sum{(r_{i+1}-r_{i}) \cdot r_{i+1}}$和$\sum{r_{i+1}-r_{i}}$。

因为每个询问的长度不同,又因为我们在上述第二三种情况都需要保证切掉第一个串,所以我们所需要提取的区间也不同,这个我们直接在平衡树上乱搞一下就可以了。

之后我们就可以愉快的AC掉这道好毒瘤题啦!

 #include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 100500
#define LL long long
using namespace std; int n,m;
char s[N];
struct data{
int l,r;
LL ans;
}d[];
vector <int> V[N<<]; #define siz(_) ((!(_))?(0):((_)->size))
#define tp pair<Treap *,Treap *>
struct Treap{
Treap *ch[];
int key,size,val,maxn,minn,val2,sum2;
LL val1,sum1;
//sum1=(r[i+1]-r[i])*r[i+1];
//sum2=(r[i+1]-r[i]);
Treap(int x){
val=maxn=minn=x;
key=rand();size=;
sum1=val1=sum2=val2=;
ch[]=ch[]=NULL;
}
void pushup(){
size=siz(ch[])+siz(ch[])+;
minn=maxn=val;
sum1=val1;sum2=val2;
if(ch[]){
minn=ch[]->minn;
sum1+=ch[]->sum1;
sum2+=ch[]->sum2;
}
if(ch[]){
maxn=ch[]->maxn;
sum1+=ch[]->sum1;
sum2+=ch[]->sum2;
}
}
}*root[N<<]; Treap *merge(Treap *a,Treap *b){
if(!a)return b;
if(!b)return a;
if(a->key<=b->key){
a->ch[]=merge(a->ch[],b);
a->pushup();return a;
}
else{
b->ch[]=merge(a,b->ch[]);
b->pushup();return b;
}
}
tp split(Treap *a,int k){
if(!a)return tp(NULL,NULL);
tp x;
if(siz(a->ch[])>=k){
x=split(a->ch[],k);
a->ch[]=x.second;
a->pushup();x.second=a;
}
else{
x=split(a->ch[],k-siz(a->ch[])-);
a->ch[]=x.first;
a->pushup();x.first=a;
}
return x;
}
int getrank(Treap *rt,int x){
int k=;
while(){
if(!rt)return k;
if(rt->val<x)k+=siz(rt->ch[])+,rt=rt->ch[];
else rt=rt->ch[];
}
}
void insert(Treap *&rt,int x){
int k=getrank(rt,x);
tp t=split(rt,k);
Treap *now=new Treap(x);
if(t.first){
tp w=split(t.first,k-);
now->val1=now->sum1=1ll*(x-w.second->val)*x;
now->val2=now->sum2=(x-w.second->val);
t.first=merge(w.first,w.second);
}
if(t.second){
tp w=split(t.second,);
w.first->val1=w.first->sum1=1ll*(w.first->val-x)*w.first->val;
w.first->val2=w.first->sum2=(w.first->val-x);
t.second=merge(w.first,w.second);
}
rt=merge(merge(t.first,now),t.second);
}
void dfs(Treap *o,Treap *&rt){
if(!o)return ;
dfs(o->ch[],rt);
insert(rt,o->val);
dfs(o->ch[],rt);
}
int find1(Treap *rt,int x){
if(rt==NULL)return ;
if(rt->val>=x)return find1(rt->ch[],x);
else{
if(!rt->ch[]||rt->ch[]->minn>=x)return rt->val;
return find1(rt->ch[],x);
}
}
int find2(Treap *rt,int x){
if(rt==NULL)return ;
if(rt->val<=x)return find2(rt->ch[],x);
else{
if(!rt->ch[]||rt->ch[]->maxn<=x)return rt->val;
return find2(rt->ch[],x);
}
}
void query(Treap *rt,int x,LL &s1,int &s2){
if(!rt)return ;
if(rt->val<=x){
if(rt->ch[])s1+=rt->ch[]->sum1,s2+=rt->ch[]->sum2;
s1+=rt->val1,s2+=rt->val2;
query(rt->ch[],x,s1,s2);
}
else query(rt->ch[],x,s1,s2);
}
LL query(int x,int l){
LL ans=;
int r1=root[x]->minn,r2=root[x]->maxn;
if(r2-l+<r1){
ans+=1ll*(r1-l)*(r1-(r2-l+));
ans+=1ll*(n-(r2-l)-+n-r1)*(r1-r2+l-)/;
}
int pos1=find1(root[x],r2-l+);
int pos2=find2(root[x],r1+l-);
if(!pos2)pos2=r2;
LL sum1=,sum2=;
int size1=,size2=;
query(root[x],pos1,sum1,size1);
query(root[x],pos2,sum2,size2);
if(pos2>pos1){
ans+=sum2-sum1;
ans-=1ll*(r2-l+)*(size2-size1);
if(pos2-l+>r1)ans-=1ll*((pos2-l+)-r1)*(pos2-(r2-l+));
}
return ans;
}
int last[N],tot,mx[N<<],ch[N<<][],par[N<<]; void extend(int x,int c){
int p=last[x-],np=++tot;
mx[np]=mx[p]+;
root[np]=NULL;
insert(root[np],x);
for(;p&&!ch[p][c];p=par[p])ch[p][c]=np;
if(!p) par[np]=;
else{
int q=ch[p][c];
if(mx[q]==mx[p]+)par[np]=q;
else{
int nq=++tot;
par[nq]=par[q];
memcpy(ch[nq],ch[q],sizeof ch[nq]);
mx[nq]=mx[p]+;
root[nq]=NULL;
par[q]=par[np]=nq;
for(;p&&ch[p][c]==q;p=par[p])ch[p][c]=nq;
}
}
last[x]=np;
}
int e=,head[N<<];
struct edge{
int v,next;
}ed[N<<];
void add(int u,int v){
ed[e].v=v;ed[e].next=head[u];
head[u]=e++;
}
int fa[N<<][];
void dfs(int x,int d){
for(int i=;(<<i)<=d;i++)
fa[x][i]=fa[fa[x][i-]][i-];
for(int i=head[x];i;i=ed[i].next){
fa[ed[i].v][]=x;
dfs(ed[i].v,d+);
}
}
int find(int x,int l){
for(int i=;~i;i--)
if(mx[fa[x][i]]>=l)x=fa[x][i];
return x;
}
void dfs(int x){
for(int i=head[x];i;i=ed[i].next){
dfs(ed[i].v);
if(siz(root[ed[i].v])>siz(root[x]))swap(root[x],root[ed[i].v]);
dfs(root[ed[i].v],root[x]);
}
for(int i=;i<V[x].size();i++){
int now=V[x][i];
d[now].ans=query(x,d[now].r-d[now].l+);
}
}
int main(){
scanf("%d%d",&n,&m);
scanf("%s",s+);
last[]=++tot;
for(int i=;i<=n;i++)extend(i,s[i]-'');
for(int i=;i<=tot;i++)add(par[i],i);
dfs(,);
for(int i=,x;i<=m;i++){
scanf("%d%d",&d[i].l,&d[i].r);
x=find(last[d[i].r],d[i].r-d[i].l+);
V[x].push_back(i);
}
dfs();
LL all=1ll*(n-)*(n-)/;
for(int i=;i<=m;i++){
d[i].ans=all-d[i].ans;
printf("%lld\n",d[i].ans);
}
return ;
}

bzoj5253 [2018多省省队联测]制胡窜的更多相关文章

  1. BZOJ_5249_Luogu_P4364_[2018多省省队联测]_IIIDX_九省联考2018_JLOI2018_线段树

    BZOJ_5249_[2018多省省队联测]IIIDX_线段树 Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐 ...

  2. 5249: [2018多省省队联测]IIIDX

    5249: [2018多省省队联测]IIIDX 链接 分析: 贪心. 将给定的权值从大到小排序,从第一个往后挨个赋值,考虑第i个位置可以赋值那些树.首先满足前面必须至少有siz[i]个权值没选,如果存 ...

  3. bzoj 5249 [2018多省省队联测] IIIDX

    bzoj 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 ...

  4. Loj #2479. 「九省联考 2018」制胡窜

    Loj #2479. 「九省联考 2018」制胡窜 题目描述 对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度. 接着,我们定义 \(S_i\) 表示 \(S\) 中第 ...

  5. 【刷题】BZOJ 5248 [2018多省省队联测]一双木棋

    Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...

  6. bzoj 5248: [2018多省省队联测]一双木棋

    Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...

  7. bzoj 5251: [2018多省省队联测]劈配

    Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayi ...

  8. bzoj5252 [2018多省省队联测]林克卡特树

    斜率优化树形dp?? 我们先将问题转化成在树上选K+1条互不相交路径,使其权值和最大. 然后我们考虑60分的dp,直接维护每个点子树内选了几条路径,然后该点和0/1/2条路径相连 然后我们会发现最后的 ...

  9. bzoj5251 [2018多省省队联测]劈配

    直接网络流模拟即可AC. 可持久化+暴力=90分, 可持久化+二分=30分, 暴力加边+二分=100分. 我也很无奈啊. Ivan便涨红了脸,额上的青筋条条绽出,争辩道,“memcpy也是可持久化…… ...

随机推荐

  1. aes加解密 Illegal key size

    做aes加密时,发生一个奇怪的错误,在本地环境是好的,发布到测试环境就出问题, java.security.InvalidKeyException: Illegal key size 想到本地环境之前 ...

  2. [ SSH框架 ] Hibernate框架学习之三

    一.表关系的分析 Hibernate框架实现了ORM的思想,将关系数据库中表的数据映射成对象,使开发人员把对数据库的操作转化为对对象的操作,Hibernate的关联关系映射主要包括多表的映射配置.数据 ...

  3. word break II(单词切分)

    Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add space ...

  4. 浏览器调试js

    在Google Chrome浏览器出来之前,我一直使用FireFox,因为FireFox的插件非常丰富,更因为FireFox有强大的Firebug,对于前端开发可谓神器. 在Chrome出来的时候,我 ...

  5. jbpm 工作流(二)

    1           概述 本文主要介绍如何将JBPM+Struts+Spring+Hibernate整合在一块.并通过一个简单实例来说明.此实例为一个申请审批的简单流程,并将申请人和审批人记录到数 ...

  6. 前端mv框架下(目前写的是vue),对组件抽象的思考

    前沿: 抽象是门大学问.前端mv框架中,以组件化的概念为主.经常会考虑抽象到组件级别,进行复用.合理的抽象,能提高效率,减少业务逻辑视图的耦合程度.不合理的抽象,则会增加代码的复杂程度. 遇到的问题 ...

  7. Access Treeview树节点代码一

    Private Sub TreeView0_Updated(Code As Integer)Dim ndeindex As NodeSet ndeindex = TreeView0.Nodes.Add ...

  8. Ocelot中文文档-转换Claims

    Ocelot允许用户访问claims并把它们转换到头部,请求字符串参数和其他claims中.这仅在用户通过身份验证后才可用. 用户通过身份验证之后,我们运行claims转换中间件.这个中间件允许在授权 ...

  9. RDC去省赛玩前の日常训练 Chapter 1

    4/3 技能点 A. 生成树的计数 论文:周冬<生成树的计数及其应用>(看不懂 pending) 一个栗子:Codeforces 719D 两个做法 Matrix-Tree + 高斯消元, ...

  10. 原生Feign使用详解

    一,简介 Feign使得 Java HTTP 客户端编写更方便.Feign 灵感来源于Retrofit.JAXRS-2.0和WebSocket.Feign最初是为了降低统一绑定Denominator到 ...