正着做着实不太好做,正难则反,考虑反着做。

把i,j看成在切割字符串,我们统计有多少对(i,j)会切割所有与\(s_{l,r}\)相同的串。对于在后缀自动机上表示\(s_{l,r}\)的节点x,x的parent子树内的endpos节点集合,就是和\(s_{l,r}\)相等的串的最后一个字符的出现位置。我们相当于在s串里得到了若干个线段,每个线段表示的子串都和\(s_{l,r}\)相等,然后用两刀把这些串都割了。我们分最左边的串和最右边的串是否存在交集进行讨论。

如果存在交集,线段数量是m

1.第一刀切串[1,i],第二刀切[i+1,m],方案数\((r_{i+1}-r_{i})(r_{i+1}-l_{m})\)

2.第一刀切[1,m],第二刀在第一刀右面随便切,是一个等差数列

3.第一刀切在第一个串左边,第二刀切在交集,一个乘法原理

如果不存在交集

可行的位置收到了限制,我们要求第一刀必须切第一个串,第二刀必须切第m个串,我们讨论出第一刀可行的线段编号区间[L,R],再统计方案数。

总之两种情况都需要维护\(\sum_{i=L}^{R}(r_{i+1}-r_{i})(r_{i+1}-l_{m})\)这个式子,把它拆开。

\[\sum_{i=L}^{R}(r_{i+1}-r_{i})(r_{i+1}-l_{m})
\\=\sum_{i=L}^{R}(\ (r_{i+1}^{2}-r_{i}r_{i+1})-l_{m}(r_{i+1}-r_{i})\ )
\\=\sum_{i=L}^{R}(r_{i+1}^{2}-r_{i}r_{i+1})-l_{m}(r_{R}-r_{L})
\]

常用套路,用线段树合并维护endpos集合,和式第二项维护相邻两项的乘积,对应pushup时左区间max和右区间min,我们需要维护一段区间内最大/最小值,再维护和式即可

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std; template <typename _T> void read(_T &ret)
{
ret=0; _T fh=1; char c=getchar();
while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); }
while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); }
ret=ret*fh;
}
const int N1=1e5+5, S1=N1*2, M1=S1*70, inf=0x3f3f3f3f;
struct EDGE{
int to[S1],nxt[S1],head[S1],cte;
void ae(int u,int v)
{ cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; }
}e;
struct node{ ll sum; int mi,ma;
friend node operator + (const node &s1,const node &s2)
{ return (node){s1.sum+s2.sum-((s2.mi!=inf)?1ll*s1.ma*s2.mi:0ll), min(s1.mi,s2.mi) , max(s1.ma,s2.ma)}; }
}; int n,Q;
char str[N1];
int idx(char c){ return c-'0'; } struct SEG{
int mi[M1],ma[M1],ls[M1],rs[M1],root[S1],tot; ll sum[M1];
void init(){ mi[0]=inf; }
void pushup(int rt)
{
mi[rt]=min(mi[ls[rt]],mi[rs[rt]]);
ma[rt]=max(ma[ls[rt]],ma[rs[rt]]);
sum[rt]=sum[ls[rt]]+sum[rs[rt]];
if(mi[rs[rt]]!=inf) sum[rt]-=1ll*ma[ls[rt]]*mi[rs[rt]];
}
void ins(int x,int l,int r,int &rt)
{
if(!rt) rt=++tot;
if(l==r){ mi[rt]=ma[rt]=l; sum[rt]=1ll*l*l; return; }
int mid=(l+r)>>1;
if(x<=mid) ins(x,l,mid,ls[rt]);
else ins(x,mid+1,r,rs[rt]);
pushup(rt);
}
//位置互不相同 在线段树叶节点一定会return 无需额外特判
int merge(int r1,int r2)
{
if(!r1||!r2) return r1+r2;
int rt=++tot;
ls[rt]=merge(ls[r1],ls[r2]);
rs[rt]=merge(rs[r1],rs[r2]);
pushup(rt);
return rt;
}
int lower(int x,int l,int r,int rt)
{
if(l==r){
if(mi[rt]<=x) return mi[rt];
else return -1;
}
int mid=(l+r)>>1;
if(mi[rs[rt]]<=x) return lower(x,mid+1,r,rs[rt]);
else return lower(x,l,mid,ls[rt]);
}
int upper(int x,int l,int r,int rt)
{
if(l==r){
if(ma[rt]>=x) return ma[rt];
else return -1;
}
int mid=(l+r)>>1;
if(ma[ls[rt]]>=x) return upper(x,l,mid,ls[rt]);
else return upper(x,mid+1,r,rs[rt]);
}
node query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R){
return (node){sum[rt],mi[rt],ma[rt]};
}
int mid=(l+r)>>1; node ans=(node){0ll,inf,0};
if(L<=mid) ans=(ans+query(L,R,l,mid,ls[rt]));
if(R>mid) ans=(ans+query(L,R,mid+1,r,rs[rt]));
return ans;
}
}s; int trs[S1][10],pre[S1],dep[S1],id[S1],tot,la;
void init(){ tot=la=1; }
void insert(int c,int i)
{
int p=la,np=++tot,q,nq; la=np;
dep[np]=dep[p]+1;
s.ins(i,1,n,s.root[np]); id[i]=np;
for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
if(!p){ pre[np]=1; return; }
q=trs[p][c];
if(dep[q]==dep[p]+1) pre[np]=q;
else{
pre[nq=++tot]=pre[q];
pre[q]=pre[np]=nq;
dep[nq]=dep[p]+1;
memcpy(trs[nq],trs[q],sizeof(trs[nq]));
for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
}
}
int ff[S1][19];
void dfs(int x)
{
for(int j=2;j<=18;j++) ff[x][j]=ff[ ff[x][j-1] ][j-1];
for(int j=e.head[x];j;j=e.nxt[j]){
int v=e.to[j];
dfs(v);
s.root[x]=s.merge(s.root[x],s.root[v]);
}
}
void build()
{
for(int i=2;i<=tot;i++) e.ae(pre[i],i), ff[i][0]=i, ff[i][1]=pre[i];
dfs(1);
} int main()
{
// freopen("1.in","r",stdin);
read(n); read(Q);
scanf("%s",str+1);
init(); s.init();
for(int i=1;i<=n;i++) insert(idx(str[i]),i);
build();
int l,r,x,len;
for(int q=1;q<=Q;q++){
read(l); read(r); len=r-l+1;
x=id[r];
// for(;dep[pre[x]]<=len;x=pre[x])
for(int j=18;j>=0;j--)
if(dep[ff[x][j]]>=len) x=ff[x][j];
ll ans=1ll*(n-1)*(n-2)/2,tmp=0;
int r1=s.mi[s.root[x]], rm=s.ma[s.root[x]], lm=rm-len+1, l1=r1-len+1;
if(r1>lm){ //s1与sm有交
tmp+=s.sum[s.root[x]]-1ll*r1*r1-1ll*lm*(rm-r1);
tmp+=max(0ll,1ll*(2*n-lm-1-r1)*(r1-lm)/2);
tmp+=max(0ll,1ll*(l1-1)*(r1-lm));
}else{
int L=s.lower(lm,1,n,s.root[x]);
int R=s.lower(r1+len-2,1,n,s.root[x]), lR=R-len+1;
int nxt=s.upper(R+1,1,n,s.root[x]);
if(L!=-1 && r!=-1 && L<=R){
node k=s.query(L,R,1,n,s.root[x]);
tmp+=k.sum-1ll*L*L-1ll*lm*(R-L);
tmp+=1ll*(r1-lR)*(nxt-lm);
}
}
ans-=tmp;
printf("%lld\n",ans);
}
// printf("%llu\n",(sizeof(s)+sizeof(ff)+sizeof(e)+sizeof(trs))/1024/1024);
return 0;
}

[八省联考2018]制胡窜 (SAM+大讨论)的更多相关文章

  1. [八省联考2018]林克卡特树lct——WQS二分

    [八省联考2018]林克卡特树lct 一看这种题就不是lct... 除了直径好拿分,别的都难做. 所以必须转化 突破口在于:连“0”边 对于k=0,我们求直径 k=1,对于(p,q)一定是从p出发,走 ...

  2. 【BZOJ5251】【八省联考2018】劈配(网络流,二分答案)

    [BZOJ5251][八省联考2018]劈配(网络流,二分答案) 题面 洛谷 BZOJ Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序 ...

  3. LuoguP4383 [八省联考2018]林克卡特树lct

    LuoguP4383 [八省联考2018]林克卡特树lct https://www.luogu.org/problemnew/show/P4383 分析: 题意等价于选择\(K\)条点不相交的链,使得 ...

  4. luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分)

    luoguP4383 [八省联考2018]林克卡特树(树上dp,wqs二分) Luogu 题解时间 $ k $ 条边权为 $ 0 $ 的边. 是的,边权为零. 转化成选正好 $ k+1 $ 条链. $ ...

  5. 洛谷P4383 [八省联考2018]林克卡特树lct(DP凸优化/wqs二分)

    题目描述 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做“LCT” 的挑 ...

  6. [八省联考2018] 劈配 mentor

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

  7. [八省联考2018]林克卡特树lct

    题解: zhcs的那个题基本上就是抄这个题的,不过背包的分数变成了70分.. 不过得分开来写..因为两个数组不能同时满足 背包的话就是 $f[i][j][0/1]$表示考虑i子树,取j条链,能不能向上 ...

  8. BZOJ.5251.[八省联考2018]劈配mentor(最大流)

    BZOJ 洛谷 对于每个人,每次枚举一个志愿看是否能增广即可. 对于第二问,可以保留第一问中\(n\)次增广前后的\(n\)张图,二分,在对应图上看是否能增广即可. 貌似匈牙利的某种写法比网络流优多了 ...

  9. BZOJ5252 八省联考2018林克卡特树(动态规划+wqs二分)

    假设已经linkcut完了树,答案显然是树的直径.那么考虑这条直径在原树中是怎样的.容易想到其是由原树中恰好k+1条点不相交的链(包括单个点)拼接而成的.因为这样的链显然可以通过linkcut拼接起来 ...

随机推荐

  1. [LeetCode]13.罗马数字转整数(Java)

    原题地址: roman-to-integer 题目描述: 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M ...

  2. linux可以这样玩 之 杂乱无章的随笔(不定期更新)

    文章目录 快速重命名 vim的进化 vim高亮当前行 vim列编辑 vim块编辑 vim行编辑 vim 中替换内容 vim保留当前已经编辑的内容,切换到其他用户继续编辑 修改服务的进程限制 CentO ...

  3. php base64格式的图片字符串和图片文件相互转换的代码

    在移动端上传图片的时候通常会将图片转换成base64格式的字符串提交,所以此时需要使用服务器端的程序进行转换成二进制的数据.如下PHP代码实现了图片文件和base64格式的图片字符串相互转换的方法,同 ...

  4. 不用rustup,Windows下gnu版Rust安装与开发环境配置

    写在前面 本文介绍了在不使用rustup的情况下,在Windows上安装gnu版的Rust,并配置开发环境(VSCode + rust-analyzer,CLion + IntelliJ Rust)的 ...

  5. 商业智能BI必备的特性

    商业智能BI的本质对企业来说,商业智能BI不能直接产生决策,而是利用BI工具处理后的数据来支持决策.核心是通过构建数据仓库平台,有效整合数据.组织数据,为分析决策提供支持并实现其价值. 传统的DW/O ...

  6. 大数据分析用自助式BI工具就能轻松解决,so easy!

    之前老板给了我一个任务,让我赶紧学习一下大数据分析,下个季度就要用. 赶紧看了一下日历,这离下个季度还有不到半个月的时间,而且我还没有数据分析基础,该怎么能在这么短的时间内学会大数据分析呢-- 经过多 ...

  7. 使用Xshell连接VMware上的Linux虚拟机

    转至:https://www.cnblogs.com/yenengfeng/p/13684265.html 虚拟机自己安装好,这边直接说步骤.有借鉴这篇文章 https://www.cnblogs.c ...

  8. mysql索引技术名词1-5

    目录 索引技术名词 1.回表 2.覆盖索引 3.最左匹配原则 4.索引下推 5.谓词下推 索引技术名词 1.回表 注意: 1.如果依靠主键查询,叶子结点直接存储数据----主键B+树 2.如果依靠其他 ...

  9. python面试_总结02_代码题

    - 代码题 1.创建一个函数,接收一个字符串参数,判断其做为Python标识符是否合法. 具体要求: 如果合法则输出 True,否则输出 False. 如果该字符串与Python内置的关键字,或Bif ...

  10. 图解大数据 | 海量数据库查询-Hive与HBase详解

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/84 本文地址:http://www.showmeai.tech/article-det ...