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;
    }
    
  • 相关阅读:
    Android开发总结
    LeakCanary原理分析
    机器学习
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 3. 循环
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 1. 神秘朋友
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 0. 准备工作
    远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/14584580.html
Copyright © 2011-2022 走看看