题解

老了,国赛之前敲一个后缀树上LCT和线段树都休闲的很

现在后缀树上线段树合并差点把我写死

主要思路就是后缀树+线段树合并+容斥,我相信熟练的OIer看到这已经会了

但就是不想写

但是由于我过于老年化,我还是决定记录一下我的思路

我用后缀自动机建的后缀树,所以是反串的后缀树,我考虑的都是区间字符串的结束位置

先熟练的建一个后缀树,我们对于每个区间代表的字符串,可以在后缀树上找到对应的节点,这个节点的子树里包含的结束位置往前数\(R - L + 1\)就是所有的这个区间字符串出现的位置

我们考虑反着来,好容易啊只需要统计三个区间都没有的方案呗!

30min过去了。。。咋统计啊= =

好吧,反着失败了,我们试着正着来,正着需要7个值

+至少在左边有

+至少在中间有

+至少在右边有

-至少在左边和中间有

-至少在左边和右边有

-至少在中间和右边有

+三个都有

容斥原理嘛。。

似乎可以维护了嘛

设区间长为\(l = R - L + 1\)

对于至少在左边有的

我们需要找到结束位置最小的地方\(t\),\(i >= t\)的就是合法区间

对于至少在右边有的

我们需要找到结束位置最大的地方\(t\),\(j <= t - l + 1\)的就是合法区间

对于至少在中间有的

考虑从两个相邻的位置\(b,a\),\(a >= b\)

然后他们产生的贡献是\((a - b) * (b - l)\),拆开就是\((a - b) * b - l *(a - b)\)

所以维护一个相邻位置的\((a - b) * b\)

最后减去用区间最大值减最小值乘上\(l\)

对于至少在左边和中间边有的

设\(a\)为出现最靠前的结束位置

也是两个相邻位置\(c,b\),\(b >= c\)

要求\(c - l >= a\)

贡献就是\((b - c) * (N - b)\)

那就再维护一个\((b - c) * b\)好了

至少在左边和右边有的

\(a\)为最靠前的结束位置,\(b\)为最靠后的结束位置 - l + 1

然后求左端点大于等于\(a\)

右端点小于等于\(b\)的方案数

至少在右边和中间有的

\(a\)为最靠后的结束位置-l + 1

相邻位置\(c,b\),\(b >= c\)

要求是\(b < a\)

然后统计起来是\((b - c) * (c - l)\)

维护\((b - c) * c\)事实上这是我们考虑只有中间有的时候维护的东西

三个都有的

\(a\)为最靠前的结束位置,\(b\)为最靠后的结束位置 - l + 1

一个结束位置\(c\)必须\(a + l <= c <= b - 1\)

然后相邻位置\(c,d\),有\(c >= d\)时

统计是\((c - d) * (d - l - a + 1)\)也是和考虑只有中间有维护的东西一样

维护的方式就是记录区间最大最小,合并左右区间的时候考虑中间两个点新的贡献

后五种情况都要算一下边界的区间的方案

然后就变成了,我们离线所有询问,把询问挂在节点上,然后dfs的时候合并线段树,就顺带处理了这个节点上所有询问的答案

为啥我的代码又是别人的2倍????8.3K了解一下????

实在是码农啊,服气服气,考场上给我五个点我也写不完,老了= =

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 200005
#define eps 1e-8
//#define ivorysi
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,Q;
int64 ans[300005];
char s[MAXN];
vector<pii > R[MAXN],qry[MAXN];
namespace SegmentTree {
struct node {
int lc,rc;
int minv,maxv;
int64 sum[2];
}tr[MAXN * 20];
int rt[MAXN],Ncnt;
void Init() {
tr[0].minv = N + 1,tr[0].maxv = 0,tr[0].sum[0] = tr[0].sum[1] = 0;
}
void update(int u) {
tr[u].minv = min(tr[tr[u].lc].minv,tr[tr[u].rc].minv);
tr[u].maxv = max(tr[tr[u].lc].maxv,tr[tr[u].rc].maxv);
for(int i = 0 ; i <= 1 ; ++i) tr[u].sum[i] = tr[tr[u].lc].sum[i] + tr[tr[u].rc].sum[i];
if(tr[u].lc && tr[u].rc) {
int lm = tr[tr[u].lc].maxv,rm = tr[tr[u].rc].minv;
if(lm <= rm) {
tr[u].sum[0] += 1LL * (rm - lm) * lm;
tr[u].sum[1] += 1LL * (rm - lm) * rm;
}
} }
int Merge(int u,int v,int L,int R) {
if(!u) return v;
if(!v) return u;
int mid = (L + R) >> 1;
tr[u].lc = Merge(tr[u].lc,tr[v].lc,L,mid);
tr[u].rc = Merge(tr[u].rc,tr[v].rc,mid + 1,R);
update(u);
return u;
}
void Insert(int &u,int p,int L,int R) {
if(!u) {u = ++Ncnt;}
if(L == R) {
tr[u].minv = tr[u].maxv = p;tr[u].sum[0] = tr[u].sum[1] = 0;
return;
}
int mid = (L + R) >> 1;
if(p <= mid) Insert(tr[u].lc,p,L,mid);
else Insert(tr[u].rc,p,mid + 1,R);
update(u);
} int64 Query(int u,int ql,int qr,int L,int R,int id,int &v,pii &rg) {
if(!u) return 0;
if(L == ql && R == qr) {
rg.fi = min(rg.fi,tr[u].minv);
rg.se = max(rg.se,tr[u].maxv);
int64 res = tr[u].sum[id];
if(!id) {
if(v <= tr[u].minv) {
res += 1LL * (tr[u].minv - v) * v;
}
if(tr[u].maxv <= N) v = tr[u].maxv;
}
else {
if(v >= tr[u].maxv) {
res += 1LL * (v - tr[u].maxv) * v;
}
if(tr[u].minv >= 1) v = tr[u].minv;
}
return res; }
int mid = (L + R) >> 1;
if(qr <= mid) return Query(tr[u].lc,ql,qr,L,mid,id,v,rg);
else if(ql > mid) return Query(tr[u].rc,ql,qr,mid + 1,R,id,v,rg);
else {
if(!id) return Query(tr[u].lc,ql,mid,L,mid,id,v,rg) + Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg);
else return Query(tr[u].rc,mid + 1,qr,mid + 1,R,id,v,rg) + Query(tr[u].lc,ql,mid,L,mid,id,v,rg);
}
}
}
using SegmentTree::rt;
using SegmentTree::tr;
using SegmentTree::Query;
using SegmentTree::Insert;
using SegmentTree::Merge;
namespace SuffixTree {
struct node {
int to,next,len;
}E[MAXN * 4];
int head[MAXN],sumE,ed[MAXN],dis[MAXN],fa[MAXN][20],Ncnt;
void add(int u,int v,int c) {
E[++sumE].to = v;
E[sumE].next = head[u];
E[sumE].len = c;
head[u] = sumE;
}
void dfs(int u) {
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
dis[v] = dis[u] + E[i].len;
fa[v][0] = u;
dfs(v);
}
}
void pre_Process() {
dfs(1);
for(int j = 1 ; j <= 18 ; ++j) {
for(int i = 1 ; i <= Ncnt ; ++i) {
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
for(int i = 1 ; i <= Ncnt ; ++i) {
if(ed[i]) {
for(auto t : R[ed[i]]) {
int h = ed[i] - t.fi + 1;
int u = i;
for(int l = 18 ; l >= 0 ; --l) {
if(dis[fa[u][l]] >= h) u = fa[u][l];
}
qry[u].pb(mp(h,t.se));
}
}
}
}
void Calc(int u) {
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
Calc(v);
rt[u] = Merge(rt[u],rt[v],1,N);
}
if(ed[u]) Insert(rt[u],ed[u],1,N);
for(auto k : qry[u]) {
int tmp,t;
pii rg;
int64 all = 0;
//on the left
if(tr[rt[u]].minv >= 1) {
t = tr[rt[u]].minv;
ans[k.se] += 1LL * (N - t) * (N - t - 1) / 2;
}
//on the right
if(tr[rt[u]].maxv <= N) {
t = tr[rt[u]].maxv - k.fi + 1;
ans[k.se] += 1LL * (t - 1) * (t - 2) / 2;
}
//on the middle
ans[k.se] += tr[rt[u]].sum[0];
if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
ans[k.se] -= 1LL * (tr[rt[u]].maxv - tr[rt[u]].minv) * k.fi;
ans[k.se] += 1LL * (N - tr[rt[u]].maxv) * (tr[rt[u]].maxv - k.fi);
} //on the left && middle
all = 0;
if(tr[rt[u]].minv <= N) {
rg = mp(N + 1,0);
int a = tr[rt[u]].minv;
if(a + k.fi <= N) {
all -= Query(rt[u],a + k.fi,N,1,N,1,tmp = 0,rg);
if(rg.fi <= rg.se) {
all += 1LL * N * (rg.se - rg.fi);
all += 1LL * (N - rg.fi) * (rg.fi - k.fi - a + 1);
}
}
}
ans[k.se] -= all; //on the left && right
if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
if(a <= b) ans[k.se] -= 1LL * (b - a) * (b - a - 1) / 2;
} //on the middle && right
all = 0;
if(tr[rt[u]].maxv >= 1) {
rg = mp(N + 1,0);
int a = tr[rt[u]].maxv - k.fi + 1;
if(a - 1 >= 1) {
all += Query(rt[u],1,a - 1,1,N,0,tmp = N + 1,rg);
if(rg.fi <= rg.se) {
all -= 1LL * k.fi * (rg.se - rg.fi);
all += 1LL * (a - rg.se) * (rg.se - k.fi);
}
}
}
ans[k.se] -= all; //on the left && middle && right
all = 0;
if(tr[rt[u]].minv <= tr[rt[u]].maxv) {
int a = tr[rt[u]].minv,b = tr[rt[u]].maxv - k.fi + 1;
rg = mp(N + 1,0);
if(a + k.fi <= b - 1) {
all += Query(rt[u],a + k.fi,b - 1,1,N,0,tmp = N + 1,rg);
if(rg.fi <= rg.se) {
all -= 1LL * (rg.se - rg.fi) * (k.fi + a - 1);
all += 1LL * (b - rg.se) * (rg.se - k.fi + 1 - a);
}
}
}
ans[k.se] += all;
}
}
void Solve() {
Calc(1);
for(int i = 1 ; i <= Q ; ++i) {
out(ans[i]);enter;
}
}
}
using SuffixTree::ed;
using SuffixTree::add;
namespace SAM {
struct node {
node *nxt[11],*per;
int len,cnt;
}pool[MAXN],*tail = pool,*root,*last,*que[MAXN];
int c[MAXN];
void Init() {
root = last = tail++;
root->len = 0;root->cnt = 0;root->per = NULL;
}
void build_SAM(int c,int len) {
node *nowp = tail++,*p;
nowp->len = len;nowp->cnt = 1;
for(p = last ; p && !p->nxt[c] ; p = p->per) {
p->nxt[c] = nowp;
}
if(!p) nowp->per = root;
else {
node *q = p->nxt[c];
if(q->len == p->len + 1) nowp->per = q;
else {
node *copyq = tail++;
*copyq = *q;
copyq->cnt = 0;
copyq->len = p->len + 1;
q->per = nowp->per = copyq;
for( ; p && p->nxt[c] == q ; p = p->per) {
p->nxt[c] = copyq;
}
}
}
last = nowp;
}
void build_ST() {
int m = tail - pool;
for(int i = 0 ; i < m ; ++i) {
c[pool[i].len]++;
}
for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
for(int i = 0 ; i < m ; ++i) {
que[c[pool[i].len]--] = &pool[i];
}
for(int i = m ; i >= 1 ; --i) {
if(que[i]->per) {
int u = que[i] - pool + 1,f = que[i]->per - pool + 1;
add(f,u,que[i]->len - que[i]->per->len);
if(que[i]->cnt) ed[u] = que[i]->len;
}
}
SuffixTree::Ncnt = m;
}
} void Solve() {
read(N);read(Q);
scanf("%s",s + 1);
SAM::Init();
for(int i = 1 ; i <= N ; ++i) {
SAM::build_SAM(s[i] - '0',i);
}
SAM::build_ST();
int l,r;
for(int i = 1 ; i <= Q ; ++i) {
read(l);read(r);
R[r].pb(mp(l,i));
}
SegmentTree::Init();
SuffixTree::pre_Process();
SuffixTree::Solve();
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Solve();
}

最新文章

  1. javascript 设计模式-----工厂模式
  2. [Google Guava]学习--新集合类型BiMap
  3. udhcpc命令【转】
  4. 一群猴子排成一圈,按1,2,...n 编号,数到m只,踢出局,直到剩下最后一个猴子是大王
  5. Error is 10055 由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作
  6. 使用Yeoman搭建 AngularJS 应用 (12) —— 让我们搭建一个网页应用
  7. WPF的依赖属性
  8. Javascript跳转手机站代码
  9. POJ 2774 后缀数组:查找最长公共子
  10. python编程快速上手之第8章实践项目参考答案
  11. SpringMvc4.x--Spring MVC的常用注解
  12. less的安装和使用
  13. 爬虫之selenium模拟点击
  14. 定义类型别名(typedef,using)
  15. MySql安装和基本管理&amp;mysql语句
  16. 04 - JavaSE之异常处理
  17. HDU 4758 Walk Through Squares (2013南京网络赛1011题,AC自动机+DP)
  18. BZOJ3994 约数个数和
  19. Python-Select/Poll/Epoll使用
  20. jQuery------$.each()遍历类方法

热门文章

  1. Python之Numpy数组拼接,组合,连接
  2. MT【175】刚刚凑巧
  3. mes平台Action类模版
  4. 素数筛选法(prime seive)
  5. NodeJS 笔记 URL模块
  6. 51Nod 1684 子集价值 (平方和去括号技巧)
  7. js异步处理工作机制
  8. html5 canvas(基本矩形)
  9. HDU 4506 小明系列故事——师兄帮帮忙(二分快速幂)
  10. python所有基础