转载请注明出处: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. 关于C语言中内存的3个问题

    1.程序为什么需要内存? 计算机程序 = 代码 + 结果,从宏观上理解,代码就是动作,而数据被动作加工,最终返回结果.程序是被放在内存中运行的,并且需要内存来存储一些临时变量,因此,对于程序来说,内存 ...

  2. Siki_Unity_3-8_Lua编程(未完)

    Unity 3-8 Lua编程 任务1&2&3:前言 课程内容: Lua从入门到掌握 为之后的xLua和其他热更新方案打下基础 任务4:Lua简介 Lua是轻量小巧的脚本语言--无需编 ...

  3. Nginx高性能优化

    #Nginx配置文件优化 worker_processes ; # nginx进程数,建议按照cpu数目来指定,一般为它的倍数. worker_cpu_affinity ; # 为每个进程分配CPU的 ...

  4. Vyatta 网络操作系统

    原文发表于:2010-09-19 转载至cu于:2012-07-21 以下是"开源中国社区"写到的: http://www.oschina.net/news/11423/vyatt ...

  5. Laxcus大数据操作系统单机集群版

    Laxcus大数据管理系统是我们Laxcus大数据实验室历时5年,全体系全功能设计研发的大数据产品,目前的最新版本是2.1版本.从三年前的1.0版本开始,Laxcus大数据系统投入到多个大数据和云计算 ...

  6. Pyhone学习之环境搭建

    一.python 环境搭建 本章节我们将向大家介绍如何在本地搭建Python开发环境.Python可应用于多平台包括 Linux 和 Mac OS X.你可以通过终端窗口输入 "python ...

  7. 微软职位内部推荐-Senior Software Lead-Index Gen

    微软近期Open的职位: Position: Senior Software Development Lead Bing Index Generation team is hiring! As one ...

  8. USACO 1.3.2 Barn Repair 修理牛棚(贪心)

    Description 在一个夜黑风高,下着暴风雨的夜晚,农民约翰的牛棚的屋顶.门被吹飞了. 好在许多牛正在度假,所以牛棚没有住满. 剩下的牛一个紧挨着另一个被排成一行来过夜. 有些牛棚里有牛,有些没 ...

  9. Xcode 6添加模板无效

    最近发现从Xcode 5拷贝来的模板在Xcode 6上是OK的,但是自己自定义的却不行,一直使用的是自定义的基类模板,最后发现原因是没有在 TemplateInfo.plist 中注册自定义的模板,注 ...

  10. 第七周C语言代码

    #ifndef NMN_LIST_H #define NMN_LIST_H   #include <stdio.h>   struct list_head {     struct lis ...