@description@

简述版题意:给定字符串 S 与一棵树 T,树上每个点有一个字符。求树上所有简单路径对应的字符串在 S 中的出现次数之和。

原题链接。

@solution@

一个显然的暴力:O(N^2) 枚举所有点对的字符串,建后缀自动机跑算出现次数之和。

另一个看起来比较好的算法:点分治。

每次计算经过重心的字符串,可以拆成两部分:某个点到重心的字符串 + 重心到某个点的字符串。

不过合并的时候需要在原字符串 S 上找分界点(不然无法合并),该算法执行一次复杂度为 O(M)。

接下来?发现并没有什么更好的性质可以利用。

数据范围比较小,我们不妨考虑一些玄学的操作:平衡复杂度。

在点分治时,如果当前连通块大小 < \(\sqrt{M}\) 则执行第一种暴力,否则执行第二种暴力。这样平衡下来复杂度为 \(O(N\sqrt{M})\)。

看起来比较显然:连通块大小 < \(\sqrt{M}\) 时 O(size^2) 优于 O(M);否则 O(M) 优于 O(size^2)。

至于复杂度的正确性,第一种暴力的总和显然 \(O(N\sqrt{M})\)。第二种暴力由于决策树的叶子个数 <= \(O(\sqrt{M})\),而深度为 logN(点分治),所以也是 \(O(N\sqrt{M})\)。

不过需要注意点分治时,容斥减去同一子树的贡献也需要根据子树大小分类讨论。

提一点细节:我们找某个点到重心的字符串是往前加字符,并以该字符串为后缀,更新 S 的前缀。也就是说我们不能跑 DAG,需要直接在 parent 树上跑。

其实也不是很难处理,不过状态需要存成两部分:所在结点与现长度。

转移时分两种情况考虑,一个是长度依然小于等于所在结点表示字符串的最大长度,直接在原字符串 S 上看加入这个字符是否仍然合法;另一种,我们需要处理出每个结点的最长字符串前面加入某个字符会转移到的点(可以根据儿子找父亲)。

@accepted code@

#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std; typedef long long ll; const int MAXN = 100000; #define mp make_pair
#define fi first
#define se second struct SAM{
char str[MAXN + 5]; int n;
struct node{
int len, pos, cnt, tag;
node *sn[26], *ch[26], *fa;
}pl[MAXN + 5], *ncnt, *lst, *root;
SAM() {ncnt = lst = root = pl;}
node *extend(int x, int ps) {
node *nw = (++ncnt), *p = lst; lst = nw;
nw->len = p->len + 1, nw->pos = ps, nw->cnt = 1;
while( p && p->ch[x] == NULL )
p->ch[x] = nw, p = p->fa;
if( !p ) nw->fa = root;
else {
node *q = p->ch[x];
if( p->len + 1 == q->len )
nw->fa = q;
else {
node *nq = (++ncnt); (*nq) = (*q);
nq->len = p->len + 1, nq->cnt = 0;
nw->fa = q->fa = nq;
while( p && p->ch[x] == q )
p->ch[x] = nq, p = p->fa;
}
}
return nw;
}
node *nd[MAXN + 5];
int a[MAXN + 5], b[MAXN + 5];
void build(int _n) {
n = _n;
for(int i=0;i<n;i++) nd[i] = extend(str[i] - 'a', i);
for(int i=ncnt-pl;i>=1;i--) {
node *p = &pl[i];
p->fa->sn[str[p->pos - p->fa->len] - 'a'] = p;
}
for(int i=1;i<=ncnt-pl;i++) b[pl[i].len]++;
for(int i=1;i<=n;i++) b[i] += b[i-1];
for(int i=1;i<=ncnt-pl;i++) a[b[pl[i].len]--] = i;
for(int i=ncnt-pl;i>=1;i--) pl[a[i]].fa->cnt += pl[a[i]].cnt;
}
int f[MAXN + 5];
void clear() {
for(int i=0;i<n;i++) f[i] = 0;
for(int i=0;i<=ncnt-pl;i++) pl[i].tag = 0;
}
void get() {
for(int i=1;i<=ncnt-pl;i++) pl[a[i]].tag += pl[a[i]].fa->tag;
for(int i=0;i<n;i++) f[i] = nd[i]->tag;
}
pair<node*, int>trans(pair<node*, int>x, int ch) {
if( x.fi == NULL ) return x;
x.se++;
if( x.se > x.fi->len ) {
x.fi = x.fi->sn[ch];
return x;
}
else {
if( str[x.fi->pos - x.se + 1] - 'a' != ch )
x.fi = NULL;
return x;
}
}
void update(node *k) {k->tag++;}
}S1, S2; struct edge{
int to; edge *nxt;
}edges[MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
#define rep(x) for(edge *p=adj[x];p;p=p->nxt) int N, M, SQ; ll ans;
char s[MAXN + 5]; bool vis[MAXN + 5]; int siz[MAXN + 5];
int get_size(int x, int fa) {
siz[x] = 1;
rep(x) {
if( vis[p->to] || p->to == fa ) continue;
siz[x] += get_size(p->to, x);
}
return siz[x];
}
int hvy[MAXN + 5];
int get_G(int x, int fa, int tot) {
int ret = -1; hvy[x] = tot - siz[x];
rep(x) {
if( vis[p->to] || p->to == fa ) continue;
int t = get_G(p->to, x, tot);
hvy[x] = max(hvy[x], siz[p->to]);
if( ret == -1 || hvy[t] < hvy[ret] ) ret = t;
}
if( ret == -1 || hvy[x] < hvy[ret] ) ret = x;
return ret;
}
void dfs2(int x, int f, SAM::node *nw, int type) {
nw = nw->ch[s[x] - 'a'];
if( !nw ) return ;
ans += nw->cnt * type;
rep(x) {
if( vis[p->to] || p->to == f ) continue;
dfs2(p->to, x, nw, type);
}
}
void dfs1(int x, int f) {
dfs2(x, -1, S1.root, 1);
rep(x) {
if( vis[p->to] || p->to == f ) continue;
dfs1(p->to, x);
}
} typedef pair<SAM::node*, int> pr; void dfs3(int x, int f, pr nw) {
nw = S1.trans(nw, s[x] - 'a');
if( nw.fi == NULL ) return ;
S1.update(nw.fi);
rep(x) {
if( vis[p->to] || p->to == f ) continue;
dfs3(p->to, x, nw);
}
}
void dfs4(int x, int f, pr nw) {
nw = S2.trans(nw, s[x] - 'a');
if( nw.fi == NULL ) return ;
S2.update(nw.fi);
rep(x) {
if( vis[p->to] || p->to == f ) continue;
dfs4(p->to, x, nw);
}
} SAM::node *a[MAXN + 5]; int cnt;
void dfs5(int x, int f, pr nw) {
nw = S1.trans(nw, s[x] - 'a');
if( nw.fi == NULL ) return ;
a[++cnt] = nw.fi;
rep(x) {
if( vis[p->to] || p->to == f ) continue;
dfs5(p->to, x, nw);
}
}
void divide(int x, int n) {
if( n <= SQ ) dfs1(x, -1);
else {
vis[x] = true;
S1.clear(), dfs3(x, -1, mp(S1.root, 0)), S1.get();
S2.clear(), dfs4(x, -1, mp(S2.root, 0)), S2.get();
for(int i=0;i<M;i++) ans += 1LL*S1.f[i]*S2.f[M-1-i];
rep(x) {
if( vis[p->to] ) continue;
int k = get_size(p->to, -1);
if( k <= SQ ) {
cnt = 0, dfs5(p->to, x, S1.trans(mp(S1.root, 0), s[x] - 'a'));
// int res = ans;
for(int i=1;i<=cnt;i++) dfs2(p->to, -1, a[i], -1);
// int del = 0;
// S1.clear(), dfs3(p->to, -1, S1.trans(mp(S1.root, 0), s[x] - 'a')), S1.get();
// S2.clear(), dfs4(p->to, -1, S2.trans(mp(S2.root, 0), s[x] - 'a')), S2.get();
// for(int i=0;i<M;i++) del += 1LL*S1.f[i]*S2.f[M-1-i];
// printf("%d %d\n", res - ans, del);
}
else {
S1.clear(), dfs3(p->to, -1, S1.trans(mp(S1.root, 0), s[x] - 'a')), S1.get();
S2.clear(), dfs4(p->to, -1, S2.trans(mp(S2.root, 0), s[x] - 'a')), S2.get();
for(int i=0;i<M;i++) ans -= 1LL*S1.f[i]*S2.f[M-1-i];
}
divide(get_G(p->to, -1, k), k);
}
}
} int main() {
scanf("%d%d", &N, &M), SQ = 8*(int)sqrt(M);
for(int i=1;i<N;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u - 1, v - 1);
}
scanf("%s%s", s, S1.str);
for(int i=0;i<M;i++) S2.str[i] = S1.str[M-i-1];
S1.build(M), S2.build(M), divide(get_G(0, -1, get_size(0, -1)), N);
printf("%lld\n", ans);
}

@details@

可以把分界点的大小适当调大(显然后一种算法常数更大)。

一开始 T 了还以为是常数问题,结果仔细一看发现我点分治只有第一轮找了重心,后面没有找重心就直接递归了。。。

@bzoj - 1921@ [ctsc2010]珠宝商的更多相关文章

  1. [CTSC2010]珠宝商 SAM+后缀树+点分治

    [CTSC2010]珠宝商 不错的题目 看似无法做,n<=5e4,8s,根号算法? 暴力一: n^2,+SAM上找匹配点的right集合sz,失配了直接退出 暴力二: O(m) 统计过lca=x ...

  2. P4218 [CTSC2010]珠宝商

    P4218 [CTSC2010]珠宝商 神题... 可以想到点分治,细节不写了... (学了个新姿势,sam可以在前面加字符 但是一次点分治只能做到\(O(m)\),考虑\(\sqrt n\)点分治, ...

  3. CTSC2010 珠宝商

    珠宝商 题目描述 Louis.PS 是一名精明的珠宝商,他出售的项链构造独特,很大程度上是因为他的制作方法与众不同.每次 Louis.PS 到达某个国家后,他会选择一条路径去遍历该国的城市.在到达一个 ...

  4. [BZOJ1921] [CTSC2010]珠宝商

    Description Input 第一行包含两个整数 N,M,表示城市个数及特征项链的长度. 接下来的N-1 行, 每行两个整数 x,y, 表示城市 x 与城市 y 有直接道路相连.城市由1~N进行 ...

  5. 洛谷P4218 [CTSC2010]珠宝商(后缀自动机+点分治)

    传送门 这题思路太清奇了……->题解 //minamoto #include<iostream> #include<cstdio> #include<cstring ...

  6. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  7. Bluestein's Algorithm

    网上很少有人提到,写的也很简单,事实上就是很简单... \(Bluestein's\ Algorithm\),用以解决任意长度\(DFT\). 考虑\(DFT\)的形式:\[\begin{aligne ...

  8. Sam做题记录

    Sam做题记录 Hihocoder 后缀自动机二·重复旋律5 求一个串中本质不同的子串数 显然,答案是 \(\sum len[i]-len[fa[i]]\) Hihocoder 后缀自动机三·重复旋律 ...

  9. 【BZOJ1921】【CTSC2010】珠宝商(点分治,后缀自动机)

    [BZOJ1921][CTSC2010]珠宝商(点分治,后缀自动机) 题面 洛谷 BZOJ权限题 题解 如果要我们做暴力,显然可以以某个点为根节点,然后把子树\(dfs\)一遍,建出特征串的\(SAM ...

随机推荐

  1. 手写一个简版 asp.net core

    手写一个简版 asp.net core Intro 之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架,最近参考蒋老师和 Edison 的文章和代码,结合自己对 asp ...

  2. GO 使用Webhook 实现github 自动化部署

    通常大家开发大部分是本地git push 提交,服务器上git pull 手动更新.git 可以使用webhook实现自动部署.webhook是仓库平台的一个钩子事件,通过hook 钩子监听代码,回调 ...

  3. js时间格式转换,传入时间戳,第二哥参数是格式,也可不传

    export function parseTime(time, pattern) { if (arguments.length === 0 || !time) { return null } cons ...

  4. Lambda表达式用法大比较: Scala和Java 8

    最近几年Lambda表达式风靡于编程界. 很多现代编程语言都把它作为函数式编程的基本组成部分. 基于JVM的编程语言如Scala,Groovy还有Clojure把它们作为关键部分集成在语言中.现在Ja ...

  5. 题解 P6509 【[CRCI2007-2008] JEDNAKOST】

    洛谷题目传送门!! 洛谷博客!! 这道题感觉是一个很另类的DP  至少我的做法是这样的. 重要前置思想:把A存成字符串!!! (应该也没人会想着存成int和long long 吧) 首先,我们定义状态 ...

  6. [Python基础]007.字符串

    字符串 内建操作 字符串长度 大小写变换 去空格或其他 连接字符串 查找替换 分割 判断 内建操作 字符串长度 len 代码 s = 'abcd' print len(s) 大小写变换 lower 小 ...

  7. 使用 git add -p 整理 patch

    背景 当我们修改了代码准备提交时,本地的改动可能包含了不能提交的调试语句,还可能需要拆分成多个细粒度的 pactch. 本文将介绍如何使用 git add -p 来交互式选择代码片段,辅助整理出所需的 ...

  8. VC程序设计--文字输出方法与字体示例

    在用户窗口上输出一个扇形,并在扇面竖向输出一首唐诗.本例使用绝对定位确定输出文字的位置,并采用多种自定义字体输出文字. // poemDemo.cpp : 定义应用程序的入口点. // #includ ...

  9. zabbix通过IPMI监控服务器传感器参数

    一.需求:机房dell服务器和IBM服务器皆有主板管理接口iDRAC和iMM,上周已为服务器管理接口配置了ip地址,考虑通过zabbix实现对服务器传感器参数的实时监控.使用DELL-DL1300服务 ...

  10. Rocket - util - Annotations

    https://mp.weixin.qq.com/s/7C8ZmPpwAqFqyKjL9K40Fg   介绍util中定义的注解(Annotations).   ​​   1. Annotation ...