转载请注明出处:http://www.cnblogs.com/TSHugh/p/8779709.html

YJQ的题解把思路介绍得很明白,只不过有些细节说得还是太笼统了(不过正经的题解就应该这个样子吧).
我的思路和YJQ有一些不同.
首先:

考虑反过来,求三个串都不包含询问串的方案数,这样需要的讨论会少很多.不难发现,三个串都不包含询问串的方案,就是询问串每个出现位置里(这里的位置指的是整个串,不是右端点),都至少含有一个断点的方案数.(本段落来自YJQ题解)

然后:

可以发现割的话一定是把原来的一群线段分为两波(其中某一波可以为0),假设左边的那刀为第一刀,右边的那刀为第二刀,第一波均由第一刀砍断,第二波均由第二刀砍断.
考虑如何通过划分两波线段来计算方案数.
设共匹配了m条线段,且这些线段按照位置依次排列,位置最靠前的线段为1,位置最靠后的线段为m.
考虑第一刀的位置.第一刀的位置显然是1往左,或者1上,因为如果是1往右的话是一定是切不断1的.
先看第一刀的位置在1往左的时候.这个时候第二刀必须切在所有线段的交上.这个可以很容易的判断和计算.

再来看第一刀的位置在1上的时候.先看一下上面那张图(图片来自YJQ题解),图中的1被下面线段的左端点分为了几部分——[l1,l2)、[l2,l3)、[l3,l4)、[l4,r1].不妨把1被分为的部分分为最后一段和不是最后一段两种.
先看不是最后一段的部分.每一段的贡献是|[l[i],l[i+1])|*|i+1与m的交|.写成公式就是∑(r[i+1]-r[i])*(r[i+1]-l[m]),括号拆开,所求即∑(r[i+1]-r[i])*r[i+1]与∑(r[i+1]-r[i]).(本段内容整理自YJQ题解)
再来看最后一段.考虑这一段的贡献,如果这一段是所有线段的交,那么贡献就可以用等差数列求前缀和求得,如果不是的话贡献就是|[l[i],r[1]]|*|i+1与m的交|.

(以上思路中的公式可能需要一定的微调,具体公式请读者在懂得思路后自行推断.)
以上就是我这道题思路的全部了.实现的话我用的是SAM和替罪羊启发式合并,竟然比线段树合并快……

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int A=;
const int N=;
const int M=;
const int Inf=0x3f3f3f3f;
const double alpha=0.75;
int n,m;
LL ans[M];
char s[N];
struct Q{
int to,next,id;
}q[M];
int mine[N<<],qt;
inline void add(int x,int y,int z){
q[++qt].to=y,q[qt].next=mine[x],mine[x]=qt,q[qt].id=z;
}
struct V{
int to,next;
}c[N<<];
int head[N<<],t;
inline void add(int x,int y){
c[++t].to=y,c[t].next=head[x],head[x]=t;
}
struct ScapeGoat_Tree{
ScapeGoat_Tree *ch[];
int size;
int key,max,min;
LL sum1,val1;
int sum2,val2;
inline void pushup(){
size=ch[]->size+ch[]->size+;
sum1=ch[]->sum1+ch[]->sum1+val1;
sum2=ch[]->sum2+ch[]->sum2+val2;
max=min=key;
max=std::max(max,ch[]->max);
max=std::max(max,ch[]->max);
min=std::min(min,ch[]->min);
min=std::min(min,ch[]->min);
}
inline bool isbad(){
return size*alpha+<ch[]->size||size*alpha+<ch[]->size;
}
inline void* operator new(size_t);
}*root[N<<],*null,*C,*mempool,*list[N];
inline void* ScapeGoat_Tree::operator new(size_t){
if(C==mempool){
C=new ScapeGoat_Tree[(<<)+];
mempool=C+(<<)+;
}
return C++;
}
int len;
inline void travel(ScapeGoat_Tree *p){
if(p==null)return;
travel(p->ch[]);
list[++len]=p;
travel(p->ch[]);
}
inline ScapeGoat_Tree *divide(int l,int r){
if(l>r)return null;
int mid=(l+r)>>;
list[mid]->ch[]=divide(l,mid-);
list[mid]->ch[]=divide(mid+,r);
list[mid]->pushup();
return list[mid];
}
inline void rebuild(ScapeGoat_Tree *&p){
if(p==null)return;
len=;
travel(p);
p=divide(,len);
}
inline ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,int key,LL val1,int val2){
if(p==null){
p=new ScapeGoat_Tree;
p->ch[]=p->ch[]=null;
p->size=;
p->key=p->max=p->min=key;
p->sum1=p->val1=val1;
p->sum2=p->val2=val2;
return &null;
}
ScapeGoat_Tree **ret=insert(p->ch[key>p->key],key,val1,val2);
p->pushup();
if(p->isbad())ret=&p;
return ret;
}
inline void Insert(ScapeGoat_Tree *&p,int key,LL val1,int val2){
rebuild(*insert(p,key,val1,val2));
}
inline void update(ScapeGoat_Tree *p,int key){
if(p==null)return;
if(p->key<=key)update(p->ch[],key);
else{
if(p->ch[]->size==||p->ch[]->max<=key){
p->val1=(LL)p->key*(p->key-key);
p->val2=p->key-key;
}else update(p->ch[],key);
}
p->pushup();
}
inline int upper_bound(ScapeGoat_Tree *p,int key){
if(p==null)return ;
if(p->key<=key)return upper_bound(p->ch[],key);
else{
if(p->ch[]->size==||p->ch[]->max<=key)return p->key;
else return upper_bound(p->ch[],key);
}
}
inline int lower_bound(ScapeGoat_Tree *p,int key){
if(p==null)return ;
if(p->key>=key)return lower_bound(p->ch[],key);
else{
if(p->ch[]->size==||p->ch[]->min>=key)return p->key;
else return lower_bound(p->ch[],key);
}
}
inline void Insert(ScapeGoat_Tree *&p,int key){
update(p,key);
int pr=lower_bound(p,key);
Insert(p,key,pr?(LL)(key-pr)*key:,pr?key-pr:);
}
inline void query(ScapeGoat_Tree *p,int key,LL &sum,int &size){
if(p==null)return;
if(p->key<=key){
sum+=p->ch[]->sum1+p->val1;
size+=p->ch[]->sum2+p->val2;
query(p->ch[],key,sum,size);
}else
query(p->ch[],key,sum,size);
}
inline LL query(ScapeGoat_Tree *p,int len){
int r1=p->min,rn=p->max,ln=rn-len+;
LL sum1=,sum2=,ret=;
int size1=,size2=;
query(p,r1+len-,sum1,size1);
query(p,ln,sum2,size2);
if(size1<=size2)ret=;
else ret=sum1-sum2-(LL)(size1-size2)*ln;
if(ln<r1)ret+=(LL)(r1-len)*(r1-ln);
int prv=lower_bound(p,r1+len-),nxt=upper_bound(p,prv);
if(!nxt)ret+=(LL)((n-ln-)+(n-r1))*(r1-ln)>>;
else ret+=(LL)(r1-(prv-len+))*std::max(nxt-ln,);
return ret;
}
inline void dfs(ScapeGoat_Tree *p,ScapeGoat_Tree *&to){
if(p==null)return;
Insert(to,p->key);
dfs(p->ch[],to);
dfs(p->ch[],to);
}
int rt,sz,trans[N<<][],link[N<<],max[N<<],f[N<<][A],pos[N];
#define newnode(a) (max[++sz]=(a),sz)
inline int insert(int x,int last){
int w=last,nw=newnode(max[w]+),h,nh;
while(w&&!trans[w][x])trans[w][x]=nw,w=link[w];
if(!w)
link[nw]=rt;
else{
h=trans[w][x];
if(max[h]==max[w]+)
link[nw]=h;
else{
nh=newnode(max[w]+);
memcpy(trans[nh],trans[h],);
while(w&&trans[w][x]==h)trans[w][x]=nh,w=link[w];
link[nh]=link[h],link[nw]=link[h]=nh;
}
}
return nw;
}
inline void dfs(int x,int fa){
int i;
f[x][]=fa;
root[x]=null;
for(i=;i<A;++i)
f[x][i]=f[f[x][i-]][i-];
for(i=head[x];i;i=c[i].next)
dfs(c[i].to,x);
}
inline int ipos(int x,int len){
int i;
for(i=A-;i>=;--i)
if(max[f[x][i]]>=len)
x=f[x][i];
return x;
}
inline void dfs(int x){
int i,j,v;
for(i=head[x];i;i=c[i].next){
v=c[i].to;
dfs(v);
if(root[x]->size<root[v]->size)
std::swap(root[v],root[x]);
dfs(root[v],root[x]);
}
for(i=mine[x];i;i=q[i].next)
ans[q[i].id]=query(root[x],q[i].to);
}
int main(){
rt=newnode();
null=new ScapeGoat_Tree;
memset(null,,sizeof(*null));
null->ch[]=null->ch[]=null;
null->min=Inf,null->max=-Inf;
scanf("%d%d",&n,&m);
scanf("%s",s+);
int i,last=rt,l,r,x;
for(i=;i<=n;++i)
last=pos[i]=insert(s[i]-'',last);
for(i=;i<=sz;++i)
add(link[i],i);
dfs(,);
for(i=;i<=n;++i)
Insert(root[pos[i]],i);
for(i=;i<=m;++i){
scanf("%d%d",&l,&r);
x=ipos(pos[r],r-l+);
add(x,r-l+,i);
}
dfs();
LL sum=(LL)(n-)*(n-)>>;
for(i=;i<=m;++i)
printf("%lld\n",sum-ans[i]);
return ;
}

Kod

【HEOI 2018】制胡窜的更多相关文章

  1. [八省联考2018]制胡窜 (SAM+大讨论)

    正着做着实不太好做,正难则反,考虑反着做. 把i,j看成在切割字符串,我们统计有多少对(i,j)会切割所有与\(s_{l,r}\)相同的串.对于在后缀自动机上表示\(s_{l,r}\)的节点x,x的p ...

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

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

  3. bzoj5253 [2018多省省队联测]制胡窜

    后缀自动机挺好毒瘤的题. 我们考虑哪些切点是不合法的.肯定是所有的匹配串都被切了. 我们考虑第一个切口的位置. 当第一个切口在第一个出现位置前时,第二个切口必须切掉所有的串. 当第一个切口在$l_{i ...

  4. 【LOJ】#2479. 「九省联考 2018」制胡窜

    题解 老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很 现在后缀树上线段树合并差点把我写死 主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了 但就是不想写 但是由于我过于老 ...

  5. 并不对劲的复健训练-bzoj5253:loj2479:p4384:[2018多省联考]制胡窜

    题目大意 给出一个字符串\(S\),长度为\(n\)(\(n\leq 10^5\)),\(S[l:r]\)表示\(S_l,S_{l+1}...,S_r\)这个子串.有\(m\)(\(m\leq 3\t ...

  6. 洛谷P4384 制胡窜

    这题TM是计数神题......SAM就是个板子,别脑残写错就完事了.有个技巧是快速定位子串,倍增即可. 考虑反着来,就是两个断点切割所有串,求方案数. 大概分类讨论一下......先特判掉一些情况.然 ...

  7. 【HEOI 2018】Day2 T2 林克卡特树

    题目大意: 给一个n个节点的树,然后将其分成k+1个联通块,再在每个联通块取一条路径,将其连接起来,求连接起来的路径最大权值. 题解: 考场只会20分,还都打挂了…… 60分的做法其实并不难,nk D ...

  8. 【HEOI 2018】林克卡特树

    转载请注明出处:http://www.cnblogs.com/TSHugh/p/8776179.html 先说60分的.思路题解上很清晰: 问题似乎等价于选K+1条点不相交的链哎!F(x,k,0/1/ ...

  9. [HEOI 2018]一双木棋

    题意:求对抗分数差值最大. 思路:状压dp,维护一条轮廓线,最大化分差.可以发现上一行的棋子个数永远比这一行多. #include<bits/stdc++.h> using namespa ...

随机推荐

  1. 2018百度之星开发者大赛-paddlepaddle学习

    前言 本次比赛赛题是进行人流密度的估计,因为之前看过很多人体姿态估计和目标检测的论文,隐约感觉到可以用到这次比赛上来,所以趁着现在时间比较多,赶紧报名参加了一下比赛,比赛规定用paddlepaddle ...

  2. 使用Photon引擎进行unity网络游戏开发(二)——Photon常用类介绍

    使用Photon引擎进行unity网络游戏开发(二)——Photon常用类介绍 Photon PUN Unity 网络游戏开发 Photon常用类介绍: IPunCallback PUNGIPunCa ...

  3. sendcloud golang 发送短信 示例代码

    package main import ( "fmt" "crypto/md5" "encoding/hex" "sort&quo ...

  4. 无人驾驶技术之Kalman Filter原理介绍

    基本思想 以K-1时刻的最优估计Xk-1为准,预测K时刻的状态变量Xk/k-1,同时又对该状态进行观测,得到观测变量Zk,再在预测与观之间进行分析,或者说是以观测量对预测量进行修正,从而得到K时刻的最 ...

  5. [leetcode-884-Uncommon Words from Two Sentences]

    We are given two sentences A and B.  (A sentence is a string of space separated words.  Each word co ...

  6. python爬虫调用搜索引擎及图片爬取实战

    实战三-向搜索引擎提交搜索请求 关键点:利用搜索引擎提供的接口 百度的接口:wd="要搜索的内容" 360的接口:q="要搜索的内容" 所以我们只要把我们提交给 ...

  7. 【SSH框架】之Struts2系列(一)

    微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系 1.Struts2框架概述 (1).什么是Struts2 Struts2是一种基于MVC模式的轻量 ...

  8. [二叉查找树] 1115. Counting Nodes in a BST (30)

    1115. Counting Nodes in a BST (30) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Y ...

  9. ajax跨域问题(三种解决方案)

    为什么会出现跨域 跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问.也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其 ...

  10. Robotium之“去哪儿旅行”

    Robotium基于APK自动化测试,只有APK文件,没有源代码. Eclipse 默认的debug keystore可以在Windows->Preferences->Android-&g ...