• 【HackerRank】How Many Substrings?


    https://www.hackerrank.com/challenges/how-many-substrings/problem

    题解

    似乎是被毒瘤澜澜放弃做T3的一道题(因为ASDFZ有很多人做过,当然,他换了一道更毒瘤的……)
    仓鼠在最后一天的时候提了一嘴然后我发现依旧菜菜的不会……(因为太菜模拟天天被吊打)
    仓鼠好神仙啊%%%(烤熟了味道怎么样啊

    这道题三个数据结构结合在一起使用,但是感觉却没有那么码农,就是直接把板子套一套(当然板子不熟另当别论),用namespace的话可以降低编码复杂度(给起名废留了一条活路)

    我们考虑离线

    也就是对于一个右端点r,维护左端点在([1,r])的答案

    我们直接建一个后缀树出来(是反串的后缀树……就是直接正着建后缀自动机,把父亲节点连起来就好了)

    那么当右端点往后扩展一格的时候,我们只需要考虑一下增量就好了

    如果对于一个起点v,([v,r])这个串能被统计的仅当不存在一个(p<=r)([p - r + v,p])([v,r])相等

    然后看了一眼我们画的后缀树……

    如果我们能在另外一处找到某个位置p代表的节点,这个节点和r所代表的节点的lca到根的字符串长度,就是p能影响到r的范围

    那么我们就考虑对r祖先的每个节点,维护一个在子树里除了r以外最大值

    具体操作就是每次Access一下,这一条链的值就改变了

    那么最后想一下,我们r要特殊处理的那些p,在r到根的路径上被分成一段一段的,那就是r到根上路过的所有偏爱链,我们Access的时候都会考虑到,把它们记录下来然后更新线段树就好

    我们具体操作的时候可以让线段树里每个节点x代表从x开始的本质不同的字符串有多少

    每次在前面减掉在后面出现过的串就好

    查询的时候就查询一段区间和

    你说我写的你看不懂,那你看看代码吧,如果还看不懂就画画后缀树就知道了

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #define fi first
    #define se second
    #define pii pair<int,int>
    //#define ivorysi
    #define mp make_pair
    #define pb push_back
    #define enter putchar('
    ')
    #define space putchar(' ')
    #define MAXN 100005
    using namespace std;
    typedef long long int64;
    typedef double db;
    template<class T>
    void read(T &res) {
    	res = 0;T f = 1;char c = getchar();
    	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,cnt,ver[MAXN];
    pii MK[MAXN];
    char s[MAXN];
    int64 ans[MAXN];
    struct qry_node {
    	int l,r,id;
    	friend bool operator < (qry_node &a,qry_node &b) {
    		return a.r < b.r;
    	};
    }qry[MAXN];
    namespace sam {
    	struct sam_node {
    		sam_node *nxt[26],*par;
    		int len,cnt,id;
    	}pool[MAXN * 2],*tail = pool,*root,*last,*que[MAXN * 2];
    	int c[MAXN];
    	void build_sam(int c,int l) {
    		sam_node *nowp = tail++,*p;
    		nowp->len = l;nowp->cnt = 1;
    		for(p = last ; p && !p->nxt[c] ; p = p->par) {
    			p->nxt[c] = nowp;
    		}
    		if(!p) nowp->par = root;
    		else {
    			sam_node *q = p->nxt[c];
    			if(q->len == p->len + 1) nowp->par = q;
    			else {
    				sam_node *copyq = tail++;
    				*copyq = *q;
    				copyq->cnt = 0;copyq->len = p->len + 1;
    				q->par = nowp->par = copyq;
    				for( ;  p && p->nxt[c] == q; p = p->par) {
    					p->nxt[c] = copyq;
    				}
    			}
    		}
    		last = nowp;
    	}
    }
    namespace seg_tr {
    	struct seg_tr_node {
    		int l,r;
    		int64 lz,sum;
    	}tr[MAXN * 4];
    	void build(int u,int l,int r) {
    		tr[u].l = l;tr[u].r = r;
    		tr[u].lz = tr[u].sum = 0;
    		if(l == r) return;
    		int mid = (l + r) >> 1;
    		build(u << 1,l,mid);
    		build(u << 1 | 1,mid + 1,r);
    	}
    	void add_lazy(int u,int64 v) {
    		tr[u].lz += v;tr[u].sum += v * (tr[u].r - tr[u].l + 1);
    	}
    	void push_down(int u) {
    		if(tr[u].lz) {
    			add_lazy(u << 1,tr[u].lz);
    			add_lazy(u << 1 | 1,tr[u].lz);
    			tr[u].lz = 0;
    		}
    	}
    	void update(int u) {
    		tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    	}
    	void add(int u,int l,int r,int64 v) {
    		if(tr[u].l == l && tr[u].r == r) {
    			add_lazy(u,v);return;
    		}
    		int mid = (tr[u].l + tr[u].r) >> 1;
    		push_down(u);
    		if(r <= mid) add(u << 1,l,r,v);
    		else if(l > mid) add(u << 1 | 1,l,r,v);
    		else {add(u << 1,l,mid,v);add(u << 1 | 1,mid + 1,r,v);}
    		update(u);
    	}
    	int64 query(int u,int l,int r) {
    		if(tr[u].l == l && tr[u].r == r) return tr[u].sum;
    		push_down(u);
    		int mid = (tr[u].l + tr[u].r) >> 1;
    		if(r <= mid) return query(u << 1,l,r);
    		else if(l > mid) return query(u << 1 | 1,l,r);
    		else return query(u << 1,l,mid) + query(u << 1 | 1,mid + 1,r);
    	}
    }
    namespace lct {
    	struct node {
    		node *lc,*rc,*fa;
    		int pos,cov,len;
    		void cover(int v) {
    			cov = v;
    			pos = v;
    		}
    		void push_down() {
    			if(cov) {
    				if(lc) lc->cover(cov);
    				if(rc) rc->cover(cov);
    				cov = 0;
    			}
    		}
    		void update() {
    			if(lc) pos = max(lc->pos,pos);
    			if(rc) pos = max(rc->pos,pos);
    		}
    	}*tr[MAXN * 2],*que[MAXN * 2];
    	void init() {
    		for(int i = 0 ; i <= 2 * N ; ++i) {
    			tr[i] = new node;
    			tr[i]->lc = tr[i]->rc = tr[i]->fa = NULL;
    			tr[i]->pos = tr[i]->cov = 0;
    		}
    	}
    	bool isRoot(node *u) {
    		if(!u->fa) return true;
    		else return u->fa->lc != u && u->fa->rc != u;
    	}
    	bool which(node *u) {
    		return u->fa->rc == u;
    	}
    	void rotate(node *u) {
    		node *v = u->fa,*w = v->fa;
    		if(!isRoot(v) && w) {(v == w->lc ? w->lc : w->rc) = u;}
    		node *b = u == v->lc ? u->rc : u->lc;
    		u->fa = w;v->fa = u;
    		if(b) b->fa = v;
    		if(u == v->lc) {u->rc = v;v->lc = b;}
    		else {u->lc = v;v->rc = b;}
    		v->update();
    	}
    	void Splay(node *u) {
    		int tot = 0;
    		node *x;
    		for(x = u ; !isRoot(x) ; x = x->fa) {
    			que[++tot] = u;
    		}
    		que[++tot] = x;
    		for(int i = tot ; i >= 1 ; --i) {
    			que[i]->push_down();
    		}
    		while(!isRoot(u)) {
    			if(!isRoot(u->fa)) {
    				if(which(u->fa) == which(u)) rotate(u->fa);
    				else rotate(u);
    			}
    			rotate(u);
    		}
    		u->update();
    	}
    	void Access(node *u,int v) {
    		cnt = 0;
    		u->cover(v);
    		for(node *x = NULL ; u ; x = u ,u = u->fa) {
    			Splay(u);
    			MK[++cnt] = mp(u->len,u->pos);
    			u->rc = x;
    			u->update();
    		}
    	} 
    };
    void Init() {
    	read(N);read(Q);
    	scanf("%s",s + 1);
    	lct::init();
    	seg_tr::build(1,1,N);
    	sam::root = sam::last = sam::tail++;
    	for(int i = 1 ; i <= N ; ++i) {
    		sam::build_sam(s[i] - 'a',i);
    	}
    	int m = sam::tail - sam::pool;
    	for(int i = 0 ; i < m ; ++i) {
    		sam::c[sam::pool[i].len]++;
    	}
    	for(int i = 1 ; i <= N ; ++i) {
    		sam::c[i] += sam::c[i - 1];
    	}
    	for(int i = 0 ; i < m ; ++i) {
    		sam::que[sam::c[sam::pool[i].len]--] = &sam::pool[i];
    	}
    	for(int i = 1 ; i <= m ; ++i) {
    		sam::que[i]->id = i;
    		if(sam::que[i]->cnt) ver[sam::que[i]->len] = i;
    	}
    	for(int i = 1 ; i <= m ; ++i) {
    		lct::tr[i]->len = sam::que[i]->len;
    		if(sam::que[i]->par) {
    			lct::tr[i]->fa = lct::tr[sam::que[i]->par->id];
    		}
    	}
    	int l,r;
    	for(int i = 1 ; i <= Q ; ++i) {
    		read(l);read(r);++l;++r;
    		qry[i] = (qry_node){l,r,i};
    	}
    	sort(qry + 1,qry + Q + 1);
    }
    void Solve() {
    	int p = 1;
    	for(int i = 1 ; i <= N ; ++i) {
    		seg_tr::add(1,1,i,1);
    		lct::Access(lct::tr[ver[i]],i);
    		int last = 0;
    		for(int j = cnt ; j > 1 ; --j) {
    			if(MK[j].fi == 0) continue;
    			if(MK[j].se != 0) {
    				seg_tr::add(1,MK[j].se - MK[j].fi + 1,MK[j].se - last,-1);
    			}
    			last = MK[j].fi;
    		}
    		while(p <= Q && qry[p].r == i) {
    			ans[qry[p].id] = seg_tr::query(1,qry[p].l,qry[p].r); 
    			++p;
    		}
    	}
    	for(int i = 1 ; i <= Q ; ++i) {
    		out(ans[i]);enter;
    	}
    }
    int main() {
    #ifdef ivorysi
    	freopen("f1.in","r",stdin);
    #endif
    	Init();
    	Solve();
    	return 0;
    }
    
  • 相关阅读:
    POJ-3984-迷宫问题(bfs+记录路径)
    StringBuilder与String的区别
    845. 八数码(bfs+map)
    844. 走迷宫(bfs模板)
    843. n-皇后问题(dfs+输出各种情况)
    洛谷 P1337 [JSOI2004]平衡点 / 吊打XXX
    【模板】 线性筛质数
    接文游戏
    【NOIP2011提高组】计算系数
    洛谷 P3197 [HNOI2008]越狱
  • 原文地址:https://www.cnblogs.com/ivorysi/p/9291472.html
Copyright © 2020-2023  润新知