zoukankan      html  css  js  c++  java
  • P4218 [CTSC2010]珠宝商

    题目链接

    暴力部分

    首先 (n^3) 的暴力很好想到,枚举起点,枚举终点,把串放 SAM 上随便匹配即可。DFS优化可以将复杂度优化成 (O(n^2))(没有人不会 (n^2) 的做法吧)

    另一种做法

    发现这是暴力的极限了。由于这是树上路径问题,我们考虑点分治。我们可以把路径 a --> b --> c 在 (b) (分治重心)处统计。直接把 a --> b 和 b --> c 的所有路径搞出来然后匹配即可。

    发现复杂度仍然无法接受。不易发现,((a,b),(b,c)) 能够匹配,当且仅当它们都在 (s) 中出现过,并且能够在 (s) 上某些地方的 (b) 中匹配上,贡献为这种 (b) 的位置数量。一种方法是,我们可以先将 endpos 是一个特定的集合的情况的次数先搞出来,并且将反串 endpos 是一个特定的集合的情况的次数也搞出来,然后把贡献一直推到各个 endpos 里,最后 endpos 对应相乘后求和即可。但是这样可能会计重(俩链都出现在同一个子树中),容斥一下就好。

    不过这要求我们前端加字符,求新的具有新的 endpos 集合的节点。我们发现 Parent Tree 上的父子关系实际上就是在父亲的“串”的前面加了字符。因此我们可以直接在 Parent Tree 上建出转移路径即可。当我们发现当前节点还能够容纳新的 endpos 集合时,就留在当前节点;否则按照转移路径转移。这个判断可以通过比较 (nwlen)(len[np]) 来实现。

    仔细思考一下,我们发现这种做法是 (O(n+m)) 的,其中 (n) 是当前分治区域的大小。加上 (m) 是因为我们需要在两个 SAM 上向下推一遍,推到各个 endpos 里。因此我们不能让这种方法进行太多次。

    正解

    根号分治。如果当前分治块大小大于 (sqrt{n}),就用第二种方法,否则用第一种方法。

    复杂度:(O((n+m)sqrt n))

    证明。假设 (n=2^k),那么分治最多有 (k) 层,每层大小为 (n),一共有 (2n) 个“叶子”(共 (2n)(calc))。如果我们在点分树的下一半的最上面一层用 (n^2) 暴力,复杂度为 (O((sqrt n)^3))(每次 (O((sqrt n)^2)),最多 (O(sqrt n)) 次)。对于上面的部分,它的节点数为 (O(2^{k/2})),即 (O(sqrt n)),那么复杂度为 (O(msqrt n))

    注意如果容斥子树太小,也要用暴力做法。否则一个菊花图就能卡到 (O(nm))

    关键代码:

    char ch[N], s[N];
    bool vis[N];
    struct SAM {
    	char s[N];
    	int slen;
    	int son[N][26], path[N][26], fa[N], len[N], tot, lst, mp[N], pos[N], siz[N];
    	int bin[N], id[N], tag[N];
    	vector<int> vec[N];
    	SAM()
    	inline void ins(int c)
    	void dfs(int cur) {
    		for (register uint i = 0; i < vec[cur].size(); ++i) {
    			int to = vec[cur][i]; dfs(to);
    			pos[cur] = pos[to]; path[cur][s[pos[to] - len[cur]] - 'a'] = to;
    		}
    	}
    	inline void init() {
    		slen = strlen(s + 1);
    		for (register int i = 1; i <= slen; ++i)	ins(s[i] - 'a');
    		for (register int i = 2; i <= tot; ++i)	vec[fa[i]].push_back(i);
    		dfs(1);
    		for (register int i = 1; i <= tot; ++i)	++bin[len[i]];
    		for (register int i = 1; i <= m; ++i)	bin[i] += bin[i - 1];
    		for (register int i = tot; i; --i)	id[bin[len[i]]--] = i;
    		for (register int i = tot; i > 1; --i) {
    			int p = id[i];
    			siz[fa[p]] += siz[p];
    		}
    	}
    	void dfs_sam(int cur, int faa, int np, int nwlen) {
    		if (nwlen == len[np])	np = path[np][ch[cur] - 'a'];
    		else if (ch[cur] != s[pos[np] - nwlen])	np = 0;//Attention!!!
    		if (!np)	return ;
    		++nwlen;//Attention!!!
    		++tag[np];
    		for (register int i = head[cur]; i; i = e[i].nxt) {
    			int to = e[i].to; if (to == faa || vis[to])	continue;
    			dfs_sam(to, cur, np, nwlen);
    		}
    	}
    	void Pushdown(int p) {
    		for (register uint i = 0; i < vec[p].size(); ++i)
    			tag[vec[p][i]] += tag[p], Pushdown(vec[p][i]);
    //Attention!!!!!!!
    	}
    	inline void clear_tag() {
    		memset(tag, 0, sizeof(tag));
    	}
    }A, B;
    void find_root(int cur, int faa) 
    
    void dfs_siz(int cur, int faa) {
    	++Siz;
    	siz[cur] = 1;
        ...
    }
    
    void dfs_dfs_dfs(int cur, int faa, int nw) {
    	if (A.son[nw][ch[cur] - 'a']) nw = A.son[nw][ch[cur] - 'a'], ans += A.siz[nw];
    	else	return ;
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to; if (to == faa || vis[to])	continue;
    		dfs_dfs_dfs(to, cur, nw);
    	}
    }
    
    void dfs_dfs(int cur, int faa) {
    	dfs_dfs_dfs(cur, 0, 1);
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to; if (to == faa || vis[to])	continue;
    		dfs_dfs(to, cur);
    	}
    }
    
    void dfs_of_dfs_of_dfs(int cur, int faa, int np) {
    	int c = ch[cur] - 'a';//Attention!!!
    	np = A.son[np][c];
    	if (!np)	return;
    	ans -= A.siz[np];//Attention!!!
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to; if (to == faa || vis[to])	continue;
    		dfs_of_dfs_of_dfs(to, cur, np);
    	}
    }
    int stk[N], stop, yuan;
    inline void del_ans() {
    	int np = 1;
    	for (register int i = stop; i; --i) {
    		int c = ch[stk[i]] - 'a';
    		np = A.son[np][c];
    		if (!np)	return ;
    	}
    	dfs_of_dfs_of_dfs(yuan, root, np);
    }
    void dfs_of_dfs(int cur, int faa) {
    	stk[++stop] = cur;
    	del_ans();
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to; if (to == faa || vis[to])	continue;
    		dfs_of_dfs(to, cur);
    	}
    	--stop;
    }
    
    void dfs_vis(int cur) {
    	vis[cur] = true;
        ...
    }
    
    void calc(int cur, int faa, int initc, int initlen, int type) {
    	A.dfs_sam(cur, faa, ~initc ? A.son[1][initc] : 1, initlen);
    	B.dfs_sam(cur, faa, ~initc ? B.son[1][initc] : 1, initlen);
    	A.Pushdown(1); B.Pushdown(1);
    	ll res = 0;
    	for (register int i = 1; i <= m; ++i) {
    		res += 1ll * A.tag[A.mp[i]] * B.tag[B.mp[m - i + 1]];
    	}
    	ans += res * type;
    	A.clear_tag(); B.clear_tag();
    }
    
    void dfs(int cur) {
    	Siz = 0;
    	dfs_siz(cur, 0);
    	if (Siz <= limi) {
    		dfs_dfs(cur, 0);
    		dfs_vis(cur);
    		return ;
    	}
    	calc(cur, 0, -1, 0, 1);
    	for (register int i = head[cur]; i; i = e[i].nxt) {
    		int to = e[i].to; if (vis[to])	continue;
    		if (siz[to] > limi)	calc(to, cur, ch[cur] - 'a', 1, -1);
    		else	stk[stop = 1] = cur, root = cur, yuan = to, dfs_of_dfs(to, cur), stop = 0;
    	}
        ...
    }
    
    int main() {
    ...
    	for (register int i = 1; i <= m; ++i)	B.s[i] = A.s[m - i + 1];//Attention!!!
        ...
    	dfs(root);
        ...
    }
    
  • 相关阅读:
    下午不想写代码了,写个shelve的模块使用(简单实用的小型数据库)。
    io,pickle,json,一把梭哈,把这三个模块都讲了。
    import 和 __import__的区别
    namedtuple工厂函数,创造一个像实例对象的元祖(感觉到了Python的奇妙与可爱之处)。
    写一下base64字节码转换工具。
    Python加密模块的hashlib,hmac模块介绍。
    简述Orderdict,defaultdcit,ChainMap以及MappingProxyType
    工作中碰到的小问题,如何对列表内的字典排序,以及operator.itemgetter介绍。
    最近在写一个虚拟币搬砖脚本,几条建议备注下。
    Python的re,正则表达式,希望这次比较能让我记住里面的80%以上。(未完成,待继续)
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13526204.html
Copyright © 2011-2022 走看看