zoukankan      html  css  js  c++  java
  • Solution -「洛谷 P6292」区间本质不同子串个数

    (mathcal{Description})

      Link.

      给定长度为 (n),仅包含小写字符的字符串 (s)(m) 次询问,每次询问一个子串 (s[l:r]) 的本质不同子串数量。

      (nle10^5)(mle2 imes10^5)

    (mathcal{Solution})

      有种常见的离线技巧:类似扫描线,从左至右枚举右端点 (r),维护 ([1..r,r]) 的答案。为了让 (s[1:r]) 里的每个子串都尽量参与贡献,可以钦定某个子串 (T) 在其最后出现的位置贡献答案。设其最后出现位置的右端点为 (p),则它会使 (lin[1,p-|T|+1]) 的询问 ([l,r]) 的答案增加 (1)。我们只需要维护这一过程。

      联系“本质不同子串”,容易想到使用 SAM。对于 (s) SAM 上的每个结点 (u),维护 (p_u) 表示其最后出现位置,那么右端点移动一次,设移动到 (r')(s[1:r']) 在 SAM 上对应 (u),本次移动带来的影响便是 (u) 及其 fail 树上祖先们的 (p) 值全部变为 (r'),类似 LCT 的 access 操作。进一步,我们直接使用 LCT 维护这一过程,由于在 fail 树上,一条断开或链接上的树链本质上对应着一段长度连续的子串,再结合每个子串 (T) 对答案的影响形式,可以看出树链操作会使答案区间加上或减去一个公差为 (1) 的等比数列,差分后用线段树维护区间加、区间求和即可。

      Access 均摊断边次数 (mathcal O(log n)),故有 (mathcal O(nlog n)) 次区间修改,总复杂度为 (mathcal O((m+nlog n)log n))

    (mathcal{Code})

      代码真的非常好写 awa!

    /* Clearink */
    
    #include <cstdio>
    #include <vector>
    #include <cstring>
    
    #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
    #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
    
    typedef long long LL;
    typedef std::pair<int, int> PII;
    #define fi first
    #define se second
    
    inline int rint() {
    	int x = 0, s = getchar();
    	for ( ; s < '0' || '9' < s; s = getchar() );
    	for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
    	return x;
    }
    
    inline void wint( const LL x ) {
    	if ( 9 < x ) wint( x / 10 );
    	putchar( x % 10 ^ '0' );
    }
    
    const int MAXN = 1e5, MAXM = 2e5;
    int n, m;
    LL ans[MAXM + 5];
    char s[MAXN + 5];
    std::vector<PII> ask[MAXN + 5];
    
    inline void chkmin( int& a, const int b ) { b < a && ( a = b ); }
    
    struct SuffixAutomaton {
    	static const int MAXND = MAXN << 1;
    	int node, last,
    		ch[MAXND + 5][26], fail[MAXND + 5], mx[MAXND + 5], pos[MAXN + 5];
    	SuffixAutomaton(): node( 1 ), last( 1 ) {}
    
    	inline void extend( const int id, const int c ) {
    		int cur = ++node, p = last; mx[cur] = mx[p] + 1;
    		for ( ; p && !ch[p][c]; ch[p][c] = cur, p = fail[p] );
    		if ( !p ) fail[cur] = 1;
    		else {
    			int q = ch[p][c];
    			if ( mx[q] == mx[p] + 1 ) fail[cur] = q;
    			else {
    				int r = ++node; mx[r] = mx[p] + 1, fail[r] = fail[q];
    				rep ( i, 0, 25 ) ch[r][i] = ch[q][i];
    				for ( ; ch[p][c] == q; ch[p][c] = r, p = fail[p] );
    				fail[cur] = fail[q] = r;
    			}
    		}
    		pos[id] = last = cur;
    	}
    } sam;
    
    struct SegmentTree {
    	LL sum[MAXN << 2]; int tag[MAXN << 2];
    
    	inline void pushad( const int u, const int v, const int l, const int r ) {
    		sum[u] += ( r - l + 1ll ) * v;
    		tag[u] += v;
    	}
    
    	inline void pushdn( const int u, const int l, const int r ) {
    		if ( !tag[u] ) return ;
    		int mid = l + r >> 1;
    		pushad( u << 1, tag[u], l, mid );
    		pushad( u << 1 | 1, tag[u], mid + 1, r );
    		tag[u] = 0;
    	}
    
    	inline void pushup( const int u ) {
    		sum[u] = sum[u << 1] + sum[u << 1 | 1];
    	}
    
    	inline void add( const int u, const int l, const int r,
    		const int al, const int ar, const int v ) {
    		if ( al <= l && r <= ar ) return pushad( u, v, l, r );
    		int mid = l + r >> 1; pushdn( u, l, r );
    		if ( al <= mid ) add( u << 1, l, mid, al, ar, v );
    		if ( mid < ar ) add( u << 1 | 1, mid + 1, r, al, ar, v );
    		pushup( u );
    	}
    
    	inline LL query( const int u, const int l, const int r,
    		const int ql, const int qr ) {
    		if ( ql <= l && r <= qr ) return sum[u];
    		int mid = l + r >> 1; LL ret = 0; pushdn( u, l, r );
    		if ( ql <= mid ) ret += query( u << 1, l, mid, ql, qr );
    		if ( mid < qr ) ret += query( u << 1 | 1, mid + 1, r, ql, qr );
    		return ret;
    	}
    } sgt;
    
    struct LinkCutTree {
    	static const int MAXND = MAXN << 1;
    	int fa[MAXND + 5], ch[MAXND + 5][2];
    	int len[MAXND + 5], mnl[MAXND + 5], las[MAXND + 5], tag[MAXND + 5];
    
    	inline bool nroot( const int x ) {
    		return ch[fa[x]][0] == x || ch[fa[x]][1] == x;
    	}
    
    	inline void pushup( const int x ) {
    		mnl[x] = len[x];
    		if ( ch[x][0] ) chkmin( mnl[x], mnl[ch[x][0]] );
    		if ( ch[x][1] ) chkmin( mnl[x], mnl[ch[x][1]] );
    	}
    
    	inline void pushls( const int x, const int v ) { las[x] = tag[x] = v; }
    
    	inline void pushdn( const int x ) {
    		if ( tag[x] ) {
    			if ( ch[x][0] ) pushls( ch[x][0], tag[x] );
    			if ( ch[x][1] ) pushls( ch[x][1], tag[x] );
    			tag[x] = 0;
    		}
    	}
    
    	inline void rotate( const int x ) {
    		int y = fa[x], z = fa[y], k = ch[y][1] == x;
    		pushdn( y ), pushdn( x );
    		fa[x] = z; if ( nroot( y ) ) ch[z][ch[z][1] == y] = x;
    		ch[y][k] = ch[x][!k]; if ( ch[x][!k] ) fa[ch[x][!k]] = y;
    		pushup( ch[fa[y] = x][!k] = y ), pushup( x );
    	}
    
    	inline void splay( const int x ) {
    		static int y, z, stk[MAXN + 5];
    		for ( stk[y = 1] = z = x; nroot( z ); stk[++y] = z = fa[z] );
    		for ( ; y; pushdn( stk[y--] ) );
    		for ( ; nroot( x ); rotate( x ) ) {
    			if ( nroot( y = fa[x] ) ) {
    				rotate( x ^ y ^ ch[y][0] ^ ch[fa[y]][0] ? x : y );
    			}
    		}
    	}
    
    	inline void access( int x, const int r ) {
    		int t = x;
    		for ( int y = 0; x; x = fa[y = x] ) {
    			splay( x ), ch[x][1] = y, pushup( x );
    			if ( las[x] ) {
    				sgt.add( 1, 1, n,
    					las[x] - sam.mx[x] + 1, las[x] - mnl[x] + 1, -1 );
    			}
    		}
    		splay( t ), pushls( t, r );
    		sgt.add( 1, 1, n, r - sam.mx[t] + 1, r, 1 );
    	}
    } lct;
    
    int main() {
    	scanf( "%s", s + 1 ), n = strlen( s + 1 );
    	rep ( i, 1, m = rint() ) {
    		int l = rint(), r = rint();
    		ask[r].push_back( { l, i } );
    	}
    	
    	rep ( i, 1, n ) sam.extend( i, s[i] - 'a' );
    
    	rep ( i, 1, sam.node ) {
    		lct.mnl[i] = lct.len[i] = sam.mx[lct.fa[i] = sam.fail[i]] + 1;
    	}
    
    	rep ( i, 1, n ) {
    		lct.access( sam.pos[i], i );
    		for ( PII q: ask[i] ) ans[q.se] = sgt.query( 1, 1, n, q.fi, i );
    	}
    
    	rep ( i, 1, m ) wint( ans[i] ), putchar( '
    ' );
    	return 0;
    }
    
    
  • 相关阅读:
    python实现的列表操作
    python的静态方法
    python标准库学习2
    javascript继承原型继承的例子
    jQuery高亮显示文本中重要的关键字
    表格展开伸缩
    jQuery设计思想
    python标准库学习3
    python中的继承和抽象类的实现
    表格的变色问题
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14791853.html
Copyright © 2011-2022 走看看