zoukankan      html  css  js  c++  java
  • [LG6292] 区间本质不同子串个数(M)

    https://www.luogu.com.cn/problem/P6292

    题解

    一道LCT+SAM的板子题。

    考虑一个简化版的问题:区间数颜色。

    显然可以离线后枚举右端点,然后求出左端点在 ([1,r]) 这个范围内,不同颜色的个数。我们考虑新加进来一个 (c),先对每个 (l) 都增加 (1),记 (c) 上一次出现的位置为 (lst_c),如果没有出现则为 (0)。那么在 ([1,lst_c]) 范围内的左端点都被算重,需要减少 (1)。使用数据结构维护即可。

    这道题是类似的。

    考虑增加了一个点,那么他会增加很多字符串。具体来说是其parent tree上的祖先的集合内的串。我们还是考虑维护一个 (lst) 代表每个串上一次出现的位置(这里串出现位置指右端点出现位置)。显然对于一个endpos等价类其 (lst) 永远相同,所以对于sam上每个状态维护 (lst) 即可。那么对于一次操作可以看成从根到该点全部赋值。然后考虑减去算重的部分,对于每个状态,在 ([lst-len+1,lst-len_{fa}]) 这个范围内的已经算过了,所以要减去 (1)。还是同样的维护方法。

    这样直接暴力跳父亲的复杂度会被卡成 (O(n^2log{n}))。我们发现这个过程非常像LCT的Access操作,而对于 (lst) 相同的一条链其实可以看做一个点来处理。所以模拟access的即可。复杂度均摊 (O(nlog^2{n}+mlog{n}))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=500005;
    template <typename T>
    void read(T &x) {
    	T flag=1;
    	char cha=getchar();
    	for (; '0'>cha||cha>'9'; cha=getchar()) if (cha=='-') flag=-1;
    	for (x=0; '0'<=cha&&cha<='9'; cha=getchar()) x=x*10+cha-'0';
    	x*=flag;
    }
    template <typename T>
    void cmax(T &x, T y) { x=max(x, y); }
    template <typename T>
    void cmin(T &x, T y) { x=min(x, y); }
    void file() {
    #ifdef LOCAL
    	freopen("test.in", "r", stdin);
    #endif
    }
    int n, m;
    char s[MAXN];
    int son[MAXN][26], len[MAXN], fail[MAXN], lst=1, tot=1;
    void ins(int c) {
    	int p=lst;
    	int np=lst=++tot;
    	len[np]=len[p]+1;
    	for (; p&&!son[p][c]; p=fail[p]) son[p][c]=np;
    	if (!p) fail[np]=1;
    	else {
    		int q=son[p][c];
    		if (len[q]==len[p]+1) fail[np]=q;
    		else {
    			int nq=++tot;
    			for (int i=0; i<26; i++) son[nq][i]=son[q][i];
    			fail[nq]=fail[q];
    			len[nq]=len[p]+1;
    			fail[q]=fail[np]=nq;
    			for (; p&&son[p][c]==q; p=fail[p]) son[p][c]=nq;
    		}
    	}
    }
    ll sum[MAXN<<2], add[MAXN<<2];
    void push_add(int p, int l, int r, int v) {
    	sum[p]+=1ll*(r-l+1)*v;
    	add[p]+=v;
    }
    void push_down(int p, int l, int r) {
    	if (add[p]) {
    		int mid=(l+r)>>1;
    		push_add(p<<1, l, mid, add[p]);
    		push_add(p<<1|1, mid+1, r, add[p]);
    		add[p]=0;
    	}
    }
    void push_up(int p) {
    	sum[p]=sum[p<<1]+sum[p<<1|1];
    }
    void change(int p, int l, int r, int x, int y, int v) {
    	if (l>y||r<x) return;
    	if (x<=l&&r<=y) {
    		push_add(p, l, r, v);
    		return;
    	}
    	push_down(p, l, r);
    	int mid=(l+r)>>1;
    	change(p<<1, l, mid, x, y, v);
    	change(p<<1|1, mid+1, r, x, y, v);
    	push_up(p);
    }
    ll ask(int p, int l, int r, int x, int y) {
    	if (l>y||r<x) return 0;
    	if (x<=l&&r<=y) return sum[p];
    	push_down(p, l, r);
    	int mid=(l+r)>>1;
    	return ask(p<<1, l, mid, x, y)+ask(p<<1|1, mid+1, r, x, y);
    }
    int fa[MAXN], ch[MAXN][2], val[MAXN], tag[MAXN];
    void pushtag(int p, int v) {
    	val[p]=tag[p]=v;
    }
    void pushdown(int p) {
    	if (tag[p]) {
    		if (ch[p][0]) pushtag(ch[p][0], tag[p]);
    		if (ch[p][1]) pushtag(ch[p][1], tag[p]);
    		tag[p]=0;
    	}
    }
    int get(int x) {
    	return ch[fa[x]][1]==x;
    }
    bool root(int x) {
    	return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
    }
    void update(int x) {
    	if (!root(x)) update(fa[x]);
    	pushdown(x);
    }
    void rotate(int x) {
    	int y=fa[x], z=fa[y], w=get(x);
    	if (!root(y)) ch[z][ch[z][1]==y]=x;
    	fa[x]=z;
    	ch[y][w]=ch[x][w^1]; fa[ch[x][w^1]]=y;
    	ch[x][w^1]=y; fa[y]=x;
    }
    void splay(int x) {
    	update(x);
    	for (int y=fa[x]; !root(x); rotate(x), y=fa[x]) if (!root(y)) rotate(get(x)==get(y)?y:x);
    }
    void access(int x, int v) {
    	int y;
    	change(1, 1, n, 1, v, 1);
    	for (y=0; x; y=x, x=fa[x]) {
    		splay(x); ch[x][1]=y;
    		if (val[x]) change(1, 1, n, val[x]-len[x]+1, val[x]-len[fa[x]], -1);
    	}
    	pushtag(y, v);
    }
    struct query{
    	int l, r, id;
    	friend bool operator < (query a, query b) {
    		if (a.r==b.r) return a.l<b.l;
    		return a.r<b.r;
    	}
    }q[MAXN];
    ll ans[MAXN];
    int pos[MAXN];
    int main() {
    	scanf("%s", s+1);
    	n=strlen(s+1);
    	for (int i=1; i<=n; i++) ins(s[i]-'a'), pos[i]=lst;
    	for (int i=2; i<=tot; i++) fa[i]=fail[i];
    	read(m);
    	for (int i=1; i<=m; i++) {
    		read(q[i].l); read(q[i].r); q[i].id=i;
    	}
    	sort(q+1, q+1+m);
    	int now=0;
    	for (int i=1; i<=m; i++) {
    		while (now<q[i].r) {
    			now++;
    			access(pos[now], now);
    		}
    		ans[q[i].id]=ask(1, 1, n, q[i].l, q[i].r);
    	}
    	for (int i=1; i<=m; i++) printf("%lld
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    centos6 安装 directAdmin
    rpm源码安装mysql
    linux添加自启服务(程序)
    linux配置ip的方法
    开发规范
    Video Processing and Communications:(视频处理和通信)
    低清图像变成高清图像 原理
    CentOS和Ubuntu哪个好?
    遥感图像处理与一般的图像处理的区别
    GAE、SAE与BAE的对比分析(百度云)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14584580.html
Copyright © 2011-2022 走看看