P4384 [八省联考 2018] 制胡窜(SAM)

Foreword\text{Foreword}Foreword

人都道正难则反,我偏说正也不难。

这里介绍一种正面直接统计的做法。
和补集做法相比,没有那么多的分类讨论,更多的是对问题的正向分析和逐层化简、转化,也并不麻烦。
由于需要写很多线段树的操作,码量较大,本人在210行左右(但都是较为基础的操作,写起来很舒适)。
本来这就是一道码农题不是吗!
我们开始吧。

Solution\text{Solution}Solution

前置规定:
以下设截下的串的两端点(即题面描述 i+1,j−1i+1,j-1i+1,j1)为 x,yx,yx,yx,y∈(1,n)x,y\in(1,n)x,y(1,n)
设询问串长度为 lenlenlen
s(a,b)=∑i=abis(a,b)=\sum_{i=a}^bis(a,b)=i=abia>ba>ba>b 时,规定 s(a,b)=0s(a,b)=0s(a,b)=0

(以下基本模拟我做本题的思维过程)

SA 和本题感觉不是很搭,那基本就是 SAM 了。
给的限制乍一看非常恶心,但细想想还挺有话说的。

Part 1

设询问串第一次出现的右端点 位置为 aaa,最后一次出现左端点 位置为 bbb,那么当中间截下的字符串 x>ax>ax>ay<by<by<b 的时候,必然是合法的。
SAM 反串先建出后缀树,记录出现的最早和最晚位置并向父亲传递,即可维护这两个值。
容易统计这个时候的答案,应该是:
s(1,n−a−1)+s(1,b−2)−s(1,(b−1)−(a+1)+1)s(1,n-a-1)+s(1,b-2)-s(1,(b-1)-(a+1)+1)s(1,na1)+s(1,b2)s(1,(b1)(a+1)+1)
最后减去的是为了容斥掉左右端点同时满足条件算重的方案数。

Part 2

那么现在的问题就转化为了:

x≤a,y≥bx\le a,y\ge bxa,yb 且包含询问串的字符串 (x,y)(x,y)(x,y) 对数。

这个东西并太不好做。
暴力怎么做?
暴力是不是我们固定一个 xxx,然后设左端点在 xxx 右侧的最靠左的字符串的右端点pospospos,那么 rrr 的取值范围就是 [max⁡(b,pos),n−1][\max(b,pos),n-1][max(b,pos),n1]
随着左端点右移, pospospos 单调不升,右端点可以取到的范围肯定是逐渐变大,最后变成 [b,n−1][b,n-1][b,n1]
我们尝试掐头去尾的计算这部分的答案。

Part 2.1

我们找到满足 r≤br\le brb 的最靠左询问串,设其左端点为 uuu,那么当 x≤ux\le uxu 时,yyy 的取值范围就变成了 [b,n−1][b,n-1][b,n1]
这部分的贡献是:
(u−1)(n−b)(u-1)(n-b)(u1)(nb)
需要注意一些边界情况,如果 u≥au\ge aua,那么整个 part2 的贡献就是 (a−1)(n−b)(a-1)(n-b)(a1)(nb),加上后直接返回即可。

Part 2.2

l>al>al>a 的最靠左的询问串的左端点为 sufsufsufl≤al\le ala 的最靠右的询问串左端点为 preprepre,那么当 x∈(pre,a]x\in(pre,a]x(pre,a] 时,yyy 一直是在受 sufsufsuf 这个串约束,其范围是 [suf+len−1,n−1][suf+len-1,n-1][suf+len1,n1]
这部分的贡献就是:
(a−pre)(n−suf)(a-pre)(n-suf)(apre)(nsuf)

Part 2.3

现在我们只剩下 x∈[u+1,pre]x\in[u+1,pre]x[u+1,pre] 这一段的贡献了。

我们想想最理想的情况:每个位置都有一个询问串的左端点,那么答案显然就是:
∑i=u+1pren−(i+len−1)=s(n−(pre+len−1),n−(u+1+len−1))\sum_{i=u+1}^{pre}n-(i+len-1)=s(n-(pre+len-1),n-(u+1+len-1))i=u+1pren(i+len1)=s(n(pre+len1),n(u+1+len1))
但是现实很骨感,真实答案可能会取不到这么多,那么会少多少呢?
举个栗子,设左端点出现集合为 101001101001101001,考虑它和 111111111111111111 相比少了多少答案。
不难发现,第一段长度为 111000 串使答案减少了 111,第二段答案为 222000 串使答案减少了 1+2=31+2=31+2=3
一般的,一段长度为 LLL 的极长 000 串,会使答案减少 s(1,L)s(1,L)s(1,L)
那么问题就变成求 [u+1,pre][u+1,pre][u+1,pre] 这部分 000 串减少的答案之和,利用线段树合并 startposstartposstartpos 集合即可轻松维护。
(startpos这个名字是我瞎起的,就是正常SAM的endpos)

然后本题就做完了。
时间复杂度 O((n+m)log⁡n)O((n+m)\log n)O((n+m)logn)

Code\text{Code}Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;const int N=4e5+100;
const int C=10;inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,m,k;int tag[N],mn[N],mx[N];
int pos[N];
int pl[N][20];
vector<int>v[N];#define calc(x) (1ll*(x)*((x)+1)/2)
struct node{int llen,rlen,siz;ll sum;
};
node operator + (const node &a,const node &b){node o;o.siz=a.siz+b.siz;o.llen=a.llen==a.siz?a.siz+b.llen:a.llen;o.rlen=b.rlen==b.siz?b.siz+a.rlen:b.rlen;o.sum=a.sum+b.sum;o.sum+=calc(a.rlen+b.llen)-calc(a.rlen)-calc(b.llen);return o;
}
inline node emp(int len){return (node){len,len,len,calc(len)};
}struct tree{int ls,rs;node o;
};
int rt[N],tot;
struct Seg{tree tr[N*20];#define mid ((l+r)>>1)inline void pushup(int k,int l,int r){node x=tr[k].ls?tr[tr[k].ls].o:emp(mid-l+1);node y=tr[k].rs?tr[tr[k].rs].o:emp(r-mid);tr[k].o=x+y;return;}inline int copy(int x){tr[++tot]=tr[x];return tot;}void upd(int &k,int l,int r,int p){k=copy(k);if(l==r){tr[k].o=(node){0,0,1,0};return;}if(p<=mid) upd(tr[k].ls,l,mid,p);else upd(tr[k].rs,mid+1,r,p);pushup(k,l,r);}int merge(int x,int y,int l,int r){if(!x||!y) return x|y;int now=copy(x);tr[now].ls=merge(tr[now].ls,tr[y].ls,l,mid);tr[now].rs=merge(tr[now].rs,tr[y].rs,mid+1,r);pushup(now,l,r);return now;}int findsuf(int k,int l,int r,int p){if(!k) return 0;if(l==r) return (l>=p)?l:0;if(p>mid) return findsuf(tr[k].rs,mid+1,r,p);else{int res=findsuf(tr[k].ls,l,mid,p);if(res) return res;else return findsuf(tr[k].rs,mid+1,r,p);}}int findpre(int k,int l,int r,int p){if(!k) return 0;if(l==r) return (l<=p)?l:0;if(p<=mid) return findpre(tr[k].ls,l,mid,p);else{int res=findpre(tr[k].rs,mid+1,r,p);if(res) return res;else return findpre(tr[k].ls,l,mid,p);}}node query(int k,int l,int r,int x,int y){if(!k) return emp(min(r,y)-max(l,x)+1);if(x<=l&&r<=y) return tr[k].o;if(y<=mid) return query(tr[k].ls,l,mid,x,y);else if(x>mid) return query(tr[k].rs,mid+1,r,x,y);else return query(tr[k].ls,l,mid,x,y)+query(tr[k].rs,mid+1,r,x,y);}
}seg;void dfs(int x,int fa){pl[x][0]=fa;for(int k=1;pl[x][k-1];k++) pl[x][k]=pl[pl[x][k-1]][k-1];for(int to:v[x]){dfs(to,x);mn[x]=min(mn[x],mn[to]);mx[x]=max(mx[x],mx[to]);rt[x]=seg.merge(rt[x],rt[to],1,n);}if(tag[x]){seg.upd(rt[x],1,n,tag[x]);}return;
}struct SAM{int tr[N][C],fa[N],len[N],tot,lst;SAM(){tot=lst=1;}inline void ins(int c,int id){c-='0';int cur=++tot,p=lst;lst=tot;len[cur]=len[p]+1;pos[id]=cur;tag[cur]=id;mn[cur]=mx[cur]=id;for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=cur;if(!p) fa[cur]=1;else{int q=tr[p][c];if(len[q]==len[p]+1) fa[cur]=q;else{int nq=++tot;mn[nq]=n+1;mx[nq]=0;len[nq]=len[p]+1;fa[nq]=fa[q];for(int i=0;i<C;i++) tr[nq][i]=tr[q][i];fa[cur]=fa[q]=nq;for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;}}return;}void build(){for(int i=2;i<=tot;i++) v[fa[i]].push_back(i);dfs(1,0);}int find(int l,int r){int x=pos[l],L=r-l+1;for(int k=18;k>=0;k--){if(len[pl[x][k]]>=L) x=pl[x][k];}return x;}
}sam;inline ll Sum(ll x,ll y){if(x>y) return 0;else return (x+y)*(y-x+1)/2;
}
ll work(int l,int r){int x=sam.find(l,r),len=r-l+1;int a=mn[x]+len-1,b=mx[x];ll ans=Sum(1,n-a-1)+Sum(1,b-2);int w=(b-1)-(a+1)+1;if(w>=1) ans-=calc(w);a=min(a,b);int pre=seg.findpre(rt[x],1,n,a),suf=seg.findsuf(rt[x],1,n,a+1);int u=seg.findpre(rt[x],1,n,b-len+1);if(u>=a){ans+=1ll*(a-1)*(n-b);return ans;}if(suf){ans+=1ll*(a-pre)*(n-(suf+len-1));}if(u) ans+=1ll*(u-1)*(n-b);//calc: [u+1,pre]u=max(u,1);if(u+1<=pre){ans+=Sum(n-(pre+len-1),n-(u+1+len-1));		node o=seg.query(rt[x],1,n,u+1,pre);ans-=o.sum;}return ans;
}char s[N];
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();m=read();scanf(" %s",s+1);for(int i=n;i>=1;i--) sam.ins(s[i],i);sam.build();for(int i=1;i<=m;i++){int l=read(),r=read();printf("%lld\n",work(l,r));}return 0;
}
/*
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/316896.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【.NET Core项目实战-统一认证平台】第十五章 网关篇-使用二级缓存提升性能

首先说声抱歉&#xff0c;可能是因为假期综合症&#xff08;其实就是因为懒哈&#xff09;的原因&#xff0c;已经很长时间没更新博客了&#xff0c;现在也调整的差不多了&#xff0c;准备还是以每周1-2篇的进度来更新博客&#xff0c;并完成本项目所有功能。言归正传&#xff…

树上子链(树形dp求树的直径)

树上子链 题意&#xff1a; 给定一棵树 T &#xff0c;树 T 上每个点都有一个权值。 定义一颗树的子链的大小为&#xff1a;这个子链上所有结点的权值和 。 请在树 T 中找出一条最大的子链并输出。 题解&#xff1a; 求树的直径&#xff0c;题目中存在负权值&#xff0c;树…

【无码专区1】简单路径的第二大边权(启发式合并+最小生成树)

只有std&#xff0c;没有自我实现&#xff0c;所以叫做无码专区 description 给一张无向图&#xff0c;多次询问&#xff0c;每次询问两个点之间所有简单路径&#xff08;不重复经过点&#xff09;中边权第二大&#xff08;不是严格第二大&#xff09;的权值的最小值。 数据…

# CF1572B Xor of 3(构造)

解析 你CF还是你CF 省选刷到2017再往前不是很想做了&#xff0c;就来CF玩一玩。 再次感受到被CF浅颜色构造虐的快感。 本题靠着各种乱搞特判在WA了无数次之后艹过去了。 根本没有什么正确性的玄学做法&#xff0c;但是看CF数据似乎把 nnn 较小的所有情况全都pia到数据里了&…

NET Core微服务之路:简单谈谈对ELK,Splunk,Exceptionless统一日志收集中心的心得体会...

前言日志&#xff0c;一直以来都是开发人员和运维人员最关心的问题。开发人员可通过日志记录来协助问题定位&#xff0c;运维人员可通过日志发现系统隐患&#xff0c;故障等定位问题。如果你的系统中没有日志&#xff0c;就像一个断了线的风筝&#xff0c;你永远不知道它会的落…

【无码专区2】序列划分(数学)

有std&#xff0c;但是没有自我实现&#xff0c;所以是无码专区 description 完全由数字组成的字符串 sss&#xff0c;划分成若干段&#xff0c;每一段看成一个十进制的数&#xff08;允许前导零&#xff09;求有多少种划分方法使得相邻两个数至少一个是 DDD 的倍数。对 1097…

Rinne Loves Edges

Rinne Loves Edges 题意&#xff1a; 有n给点&#xff0c;m个边&#xff0c;每个边有边权&#xff0c;给你一个点S&#xff0c;问最少花多少代价&#xff0c;可以让叶子节点无法与S点连通 题解&#xff1a; dp[u]:表示u到叶子节点的最短费用的和 dp[u]min(dp[v],w); 代码&…

.NET Core 中的 Generic Host快速使用指南

本文以自己在工作中学习和使用.net core generic-host 作一个总结。前言在创建的ASPNETCORE项目中&#xff0c;我们可以在Main()中看见&#xff0c;我们通过IWebHostBuild创建了一个IWebHost&#xff0c;而微软提供了WebHost.CreateDefaultBuilder(args)来帮助我们更轻松得创建…

CF1580B Mathematics Curriculum(笛卡尔树、树形dp)

解析 比较巧妙的一道题。 难点在于对题意的转化。 关键性质&#xff1a;符合要求的点等价于与笛卡尔树上深度为 mmm 的点。 原因也较为显然&#xff0c;考虑一个特定的点 xxx&#xff0c;当枚举全局最大值时&#xff0c;其会对 xxx 产生贡献&#xff0c;且最大值另一侧就和 xx…

CF1368G Shifting Dominoes(扫描线求矩阵的并集)

CF1368G Shifting Dominoesproblemsolutioncodeproblem 题目链接 solution 求的是最后棋盘本质不同的个数&#xff0c;而本质不同等价于两个空格位置不同。 如果想要移动一个多米诺骨牌&#xff0c;要求长边上下方有空位。 移动可以看成空位的移动。 所以我们考虑把一个 …

吉吉王国(二分+树形dp)

吉吉王国 题意&#xff1a; n个点&#xff0c;m个边&#xff0c;有边权&#xff0c;现在要求叶子节点无法与1号点连通&#xff0c;要求切断的总长度不能超过m&#xff0c;且切断的最长的长度尽可能断 题解&#xff1a; 题意的前半部分可以确定是树形dp&#xff0c;后半部分…

P3239 [HNOI2015]亚瑟王(期望)

解析 显然可以利用期望的线性性对每张牌单独计算贡献。 现在的关键就是如何求出每张牌被使用的概率。 考虑第 iii 张牌&#xff0c;如果它的前面 [1,i−1][1,i-1][1,i−1] 中有 jjj 张牌被使用&#xff0c;那么它被使用的概率就是 1−(1−pi)r−j1-(1-p_i)^{r-j}1−(1−pi​)…

微软一顿操作猛如虎,PowerShell 排名直线上升

近日&#xff0c;TIOBE 发布了 2019 年 3 月编程语言排行榜&#xff0c;PowerShell 首次进入到了榜单的 Top 50 中&#xff0c;排在第 45 位。PowerShell 是运行在 Windows 操作系统上实现对系统以及应用程序进行管理自动化的命令行脚本环境。&#xff08;PowerShell 排在了 TI…

CF1237F Balanced Domino Placements(dp+组合计数)

CF1237F Balanced Domino Placementsproblemsolutioncodeproblem 题目链接 solution 骨牌横着放会占用一行两列&#xff0c;骨牌竖着放会占用两行一列。 问题可以抽象为&#xff1a;每次可以选择连续的两行放 AAA&#xff0c;或选一行放 BBB&#xff1b;每次可以选一列放 B…

AcWing 201. 可见的点

AcWing 201. 可见的点 题意&#xff1a; 题解&#xff1a; 我们先说结论:坐标(i,j)&#xff0c;如果i和j互质&#xff0c;说明该坐标为可见 为什么&#xff1f; 我们想想什么样的坐标可见&#xff0c;什么样的会被挡住。光线是一个直线&#xff0c;在同一个直线上的点会被第一…

模板:LGV引理(线性代数)

所谓LGV引理&#xff0c;就是解决LGV问题的引理。 &#xff08;逃&#xff09; 前言 上联&#xff1a;古有学完SAM学PAM&#xff1b; 下联&#xff1a;今有学完Polya学LGV&#xff1b; 横批&#xff1a;小清新。 常被用于有向图不交路径计数问题。&#xff08;废话&#xff…

ocelot 自定义认证和授权

Intro最近又重新启动了网关项目&#xff0c;服务越来越多&#xff0c;每个服务都有一个地址&#xff0c;这无论是对于前端还是后端开发调试都是比较麻烦的&#xff0c;前端需要定义很多 baseUrl&#xff0c;而后端需要没有代码调试的时候需要对每个服务的地址都收藏着或者记在哪…

CF765F Souvenirs(势能线段树)

CF765F Souvenirsproblemsolutioncodeproblem 题目链接 solution 这个势能线段树简直是太巧妙了&#xff01;&#xff01;&#xff01;( ఠൠఠ )&#xff89; 将询问按右端点升序离线下来。 对于每一个右端点 rrr&#xff0c;维护 ansimin⁡{∣ai−aj∣,j∈[i,r]}ans_i\m…

P4849 寻找宝藏(模板:四维偏序)

stable_sort 保平安。 解析 dp方程显而易见&#xff0c;关键就是如何进行这个四维偏序的转移。 考虑三维偏序&#xff08;比如拦截导弹&#xff09;我们是如何做的&#xff1f; 先按照第一维排序&#xff0c;然后分治解决前一半&#xff0c;接下来把前一半的第一维看成0&…

AcWing 220. 最大公约数

AcWing 220. 最大公约数 题意&#xff1a; 题解&#xff1a; 题目就变成了AcWing 201. 可见的点 当然有微调&#xff0c;因为可见的点里面是从0开始&#xff0c;本题从1开始&#xff0c;所以本题中phi[1]认为是0 AcWing 201. 可见的点的题解 代码&#xff1a; #include<b…