bzoj5253 [2018多省省队联测]制胡窜
后缀自动机挺好毒瘤的题。
我们考虑哪些切点是不合法的。肯定是所有的匹配串都被切了。
我们考虑第一个切口的位置。
当第一个切口在第一个出现位置前时,第二个切口必须切掉所有的串。
当第一个切口在$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多省省队联测]制胡窜的更多相关文章
- BZOJ_5249_Luogu_P4364_[2018多省省队联测]_IIIDX_九省联考2018_JLOI2018_线段树
BZOJ_5249_[2018多省省队联测]IIIDX_线段树 Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐 ...
- 5249: [2018多省省队联测]IIIDX
5249: [2018多省省队联测]IIIDX 链接 分析: 贪心. 将给定的权值从大到小排序,从第一个往后挨个赋值,考虑第i个位置可以赋值那些树.首先满足前面必须至少有siz[i]个权值没选,如果存 ...
- bzoj 5249 [2018多省省队联测] IIIDX
bzoj 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 ...
- Loj #2479. 「九省联考 2018」制胡窜
Loj #2479. 「九省联考 2018」制胡窜 题目描述 对于一个字符串 \(S\),我们定义 \(|S|\) 表示 \(S\) 的长度. 接着,我们定义 \(S_i\) 表示 \(S\) 中第 ...
- 【刷题】BZOJ 5248 [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
- bzoj 5248: [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
- bzoj 5251: [2018多省省队联测]劈配
Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayi ...
- bzoj5252 [2018多省省队联测]林克卡特树
斜率优化树形dp?? 我们先将问题转化成在树上选K+1条互不相交路径,使其权值和最大. 然后我们考虑60分的dp,直接维护每个点子树内选了几条路径,然后该点和0/1/2条路径相连 然后我们会发现最后的 ...
- bzoj5251 [2018多省省队联测]劈配
直接网络流模拟即可AC. 可持久化+暴力=90分, 可持久化+二分=30分, 暴力加边+二分=100分. 我也很无奈啊. Ivan便涨红了脸,额上的青筋条条绽出,争辩道,“memcpy也是可持久化…… ...
随机推荐
- MOOS学习笔记3——命令行
MOOS学习笔记3--命令行 例程 /** * @code A simple example showing how to use a comms client问问怎么样 */ #include &q ...
- 工作中常用Git指令操作
常用Git指令总结 前阵子有几天好不顺,可谓是喝水都呛着,更何况被Git给呛着了,还不轻,哈哈.所以打算总结一下自己在工作使用到Git相关的东西以及和大家探讨使用GIt的心得体会.于是,关于Git的的 ...
- css选择器应用
.mynav li:not(:last-child) { margin-right: 20px; }
- 简单了解JS中的几种遍历
忙了好一段时间,项目上线后终于有那么一点点空档期静下来整理一些问题了.当我们在开发项目的时候,用到遍历的地方肯定少不了,那么我们有那么多的遍历方法,在不同情况下用那种方法会更优雅而且还没bug呢? 首 ...
- amaze UI 如何添加原生表单验证
这段时间做的一个项目,整个系统就一个页面,然后就是各种模态框,js里拼HTML代码,而且因为表单空留距离小,最后选定了amaze ui原生的表单验证 在amaze ui官网找到 表单验证. 但是ama ...
- Python并发编程的几篇文章
Python几种并发实现方案的性能比较 http://www.elias.cn/Python/PyConcurrency?from=Develop.PyConcurrency python并发编程 h ...
- 竞品调研时发现的Android新设计特性
先share两篇技术层面的文章: Android M新控件之FloatingActionButton,TextInputLayout,Snackbar,TabLayout的使用:http://blog ...
- Redis实现简单消息队列
http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...
- DDGScreenShot—图片擦除功能
写在前面 图片擦除功能,也是运用图片的绘制功能, 将图片绘制后,拿到相应的图片.当然,有一涨底图更明显 实现代码如下 /** ** 用手势擦除图片 - imageView --传图片 - bgView ...
- java之Spring(AOP)前奏-动态代理设计模式(上)
我们常常会遇到这样的事,项目经理让你为一个功能类再加一个功能A,然后你加班为这个类加上了功能A: 过了两天又来了新需求,再在A功能后面加上一个新功能B,你加班写好了这个功能B,加在了A后面:又过 了几 ...