• [NOI2018]你的名字


    题目链接
    先考虑68分的做法:
    求在A串中出现,且在B串中没出现的串的数量。
    使用容斥,用A的不同子串数减去A,B的不同公共子串数。
    先用双指针,求出A的每个位置开始,在B中最多能向后匹配多远。
    然后,问题变为,给你一些区间,问它们的子区间中有多少不同的串。
    因为每个串,都是原区间([l,r])([l,i])的后缀。((l<=i<=r))
    而后缀就是在前面去掉一些字符,就是不断走fa。
    所以可以建出fa边的树,然后定位出这些位置,求这些点到根的路径的并。
    使用树链合并即可,要注意压缩的问题。
    100分做法:与68基本相同,就是判断子串([a,b])是否出现多了一个限制((l<=a,b<=r))。
    而一个串匹配的位置,就是这个点子树中红点的集合。
    红点对应前缀,所以这个限制可以转化为前缀位置的限制。
    需要判断一个点的子树中是否有x~y的数。使用DFS序+主席树即可。

    代码:

    #include <stdio.h> 
    #include <stdlib.h> 
    #define ll long long 
    int fr[2000010],ne[2000010];
    int v[2000010],bs = 0;
    int wl[2000010],wr[2000010],tm = 1;
    int xl[2000010];
    struct SAM {
    	int trs[2000010][26],fa[2000010];
    	int len[2000010],np,sl;
    	int red[2000010];
    	SAM() {
    		np = sl = 1;
    	}
    	void clean() {
    		for (int i = 1; i <= sl; i++) {
    			fa[i] = len[i] = 0;
    			red[i] = false;
    			for (int j = 0; j < 26; j++) trs[i][j] = 0;
    		}
    		np = sl = 1;
    	}
    	void insert(char c, int wz) {
    		c -= 'a';
    		int p = np;
    		np = ++sl;
    		len[np] = len[p] + 1;
    		while (p != 0 && trs[p][c] == 0) {
    			trs[p][c] = np;
    			p = fa[p];
    		}
    		if (p == 0) fa[np] = 1;
    		else {
    			int q = trs[p][c];
    			if (len[q] == len[p] + 1) fa[np] = q;
    			else {
    				int nq = ++sl;
    				fa[nq] = fa[q];
    				fa[q] = fa[np] = nq;
    				len[nq] = len[p] + 1;
    				for (int j = 0; j < 26; j++) trs[nq][j] = trs[q][j];
    				while (p != 0 && trs[p][c] == q) {
    					trs[p][c] = nq;
    					p = fa[p];
    				}
    			}
    		}
    		red[np] = wz;
    	}
    };
    void addb(int a, int b) {
    	v[bs] = b;
    	ne[bs] = fr[a];
    	fr[a] = bs;
    	bs += 1;
    }
    SAM S,T;
    void dfs1(int u) {
    	xl[tm] = u;
    	wl[u] = tm++;
    	for (int i = fr[u]; i != -1; i = ne[i]) dfs1(v[i]);
    	wr[u] = tm;
    }
    int up[1000010];
    char zf[1000010];
    struct SPx {
    	int u,cd;
    	SPx() {}
    	SPx(int U, int Cd) {
    		u = U;
    		cd = Cd;
    	}
    };
    SPx px[1000010];
    ll baoli(int s) {
    	for (int i = 1; i <= T.sl; i++) up[i] = 0;
    	for (int i = 0; i < s; i++) {
    		int cd = px[i].cd,
    		u = px[i].u;
    		while (u != 1) {
    			if (up[u] == T.len[u] - T.len[T.fa[u]]) break;
    			if (cd - T.len[T.fa[u]] > up[u]) up[u] = cd - T.len[T.fa[u]];
    			u = T.fa[u];
    			cd = T.len[u];
    		}
    	}
    	ll rtn = 0;
    	for (int i = 2; i <= T.sl; i++) rtn += up[i];
    	return rtn;
    }
    int he[24000010],cl[24000010],cr[24000010],sl = 0;
    int jianshu(int l, int r) {
    	int rt = sl++;
    	he[rt] = 0;
    	if (l + 1 == r) return rt;
    	int m = (l + r) >> 1;
    	cl[rt] = jianshu(l, m);
    	cr[rt] = jianshu(m, r);
    	return rt;
    }
    int xiugai(int i, int l, int r, int j, int x) {
    	int rt = sl++;
    	if (l + 1 == r) {
    		he[rt] = he[i] + x;
    		return rt;
    	}
    	cl[rt] = cl[i];
    	cr[rt] = cr[i];
    	int m = (l + r) >> 1;
    	if (j < m) cl[rt] = xiugai(cl[rt], l, m, j, x);
    	else cr[rt] = xiugai(cr[rt], m, r, j, x);
    	he[rt] = he[cl[rt]] + he[cr[rt]];
    	return rt;
    }
    int chaxun(int i, int l, int r, int L, int R) {
    	if (R <= l || r <= L) return 0;
    	if (L <= l && r <= R) return he[i];
    	int m = (l + r) >> 1;
    	return chaxun(cl[i], l, m, L, R) + chaxun(cr[i], m, r, L, R);
    }
    int gen[1000010],n;
    bool zichuan(int i, int l, int r, int cd) {
    	l = l + cd - 1;
    	if (l > r) return false;
    	int z = chaxun(gen[wr[i] - 1], 1, n + 1, l, r + 1) - chaxun(gen[wl[i] - 1], 1, n + 1, l, r + 1);
    	return z > 0;
    }
    int main() {
    	scanf("%s", zf);
    	for (n = 0; zf[n] != 0; n++) S.insert(zf[n], n + 1);
    	for (int i = 1; i <= S.sl; i++) fr[i] = -1;
    	for (int i = 1; i <= S.sl; i++) addb(S.fa[i], i);
    	dfs1(1);
    	gen[0] = jianshu(1, n + 1);
    	for (int i = 1; i < tm; i++) {
    		if (S.red[xl[i]] != 0) gen[i] = xiugai(gen[i - 1], 1, n + 1, S.red[xl[i]], 1);
    		else gen[i] = gen[i - 1];
    	}
    	int m;
    	scanf("%d", &m);
    	for (int i = 0; i < m; i++) {
    		int l,r,x = 1,y = 1,s = 0;
    		scanf("%s%d%d", zf, &l, &r);
    		T.clean();
    		for (int j = 0; zf[j] != 0; j++) T.insert(zf[j], 0);
    		for (int j = 0, k = -1, c = 0; zf[j] != 0; j++) {
    			c -= 1;
    			if (x != 1 && k - j == S.len[S.fa[x]]) x = S.fa[x];
    			if (y != 1 && k - j == T.len[T.fa[y]]) y = T.fa[y];
    			if (k < j) k = j,
    			c = 0;
    			while (zf[k] != 0 && S.trs[x][zf[k] - 'a'] != 0 && zichuan(S.trs[x][zf[k] - 'a'], l, r, c + 1)) {
    				c += 1;
    				x = S.trs[x][zf[k] - 'a'];
    				y = T.trs[y][zf[k] - 'a'];
    				px[s++] = SPx(y, c);
    				k += 1;
    			}
    		}
    		ll zo = 0;
    		for (int j = 1; j <= T.sl; j++) zo = zo + T.len[j] - T.len[T.fa[j]];
    		printf("%lld
    ", zo - baoli(s));
    	}
    	return 0;
    }
    
  • 相关阅读:
    P5737 【深基7.例3】闰年展示
    P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here
    P1597 语句解析
    P5735 【深基7.例1】距离函数
    P1553 数字反转(升级版)
    P1598 垂直柱状图
    P1603 斯诺登的密码
    P5738 【深基7.例4】歌唱比赛
    Ext.GridPanel 用法总结(一)—— Grid基本用法
    使用CodeSmith快速规范开发.Net软件
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11354710.html
Copyright © 2020-2023  润新知