题目大意

你们自己感受一下原题的画风...

我怀疑出题人当年就是语文爆零的

下面复述一下出题人的意思:

  • 操作1: 给你一个点集, 要你在trie上找到所有这样的点, 满足点集中存在某个点所表示的字符串是这个点所表示字符串的后缀. 把这些点的权值加一;
  • 操作2: 给你一个点集, 要你在trie上找到所有这样的点, 满足这个点所表示的字符串是点集中某个点的后缀; 求所有这些点的权值之和.

做法很显然, 我们先建立出后缀树, 在后缀树上树剖, 剖出的序列用线段树维护即可.

注意树剖得到的序列中, 一个点所表示的子树是连在一起的, 因此可以直接修改子树.

Solution

后缀自动机上树剖.

题目描述根本就不可看

#include <cstdio>
#include <cctype>
#include <deque>
#include <cstring>
#include <vector>
#include <algorithm> using namespace std;
namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1; char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
inline int getChar()
{
char c; while(! isgraph(c = getchar())); return c;
}
inline void print(int a) {if(! a) return; print(a / 10); putchar('0' + a % 10);}
inline void println(int a)
{
if(a < 0) putchar('-'); if(! a) putchar('0');
print(a);
putchar('\n');
}
}
const int N = (int)1e5;
int tp;
struct segmentTree
{
struct node
{
int tg, sz;
long long sum;
}nd[N << 2 << 2];
inline segmentTree() {memset(nd, 0, sizeof(nd));}
void addSize(int u, int L, int R, int pos)
{
++ nd[u].sz;
if(L == R) return;
if(pos <= L + R >> 1) addSize(u << 1, L, L + R >> 1, pos); else addSize(u << 1 | 1, (L + R >> 1) + 1, R, pos);
}
inline void addSize(int pos) {addSize(1, 0, tp - 1, pos);}
inline void pushDown(int u)
{
nd[u << 1].tg += nd[u].tg; nd[u << 1].sum += nd[u << 1].sz * nd[u].tg;
nd[u << 1 | 1].tg += nd[u].tg; nd[u << 1 | 1].sum += nd[u << 1 | 1].sz * nd[u].tg;
nd[u].tg = 0;
}
void modify(int u, int curL, int curR, int L, int R, int x)
{
if(curL >= L && curR <= R) {nd[u].tg += x; nd[u].sum += nd[u].sz * x; return;}
pushDown(u);
int mid = curL + curR >> 1;
if(L <= mid) modify(u << 1, curL, mid, L, R, x);
if(R > mid) modify(u << 1 | 1, mid + 1, curR, L, R, x);
nd[u].sum = nd[u << 1].sum + nd[u << 1 | 1].sum;
}
inline void modify(int L, int R, int x) {modify(1, 0, tp - 1, L, R, x);}
long long query(int u, int curL, int curR, int L, int R)
{
if(curL >= L && curR <= R) return nd[u].sum;
pushDown(u);
int mid = curL + curR >> 1; long long res = 0;
if(L <= mid) res += query(u << 1, curL, mid, L, R);
if(R > mid) res += query(u << 1 | 1, mid + 1, curR, L, R);
return res;
}
long long query(int L, int R) {return query(1, 0, tp - 1, L, R);}
}seg;
struct node
{
int suc[26], pre, len, isReal;
int vst;
vector<int> successorOnSuffixTree;
int sz, hvy, dep, tp, id, L, R;
inline node() {memset(suc, -1, sizeof(suc)); pre = -1; vst = isReal = 0; successorOnSuffixTree.clear();}
}nd[N << 2];
inline int cmp(int a, int b) {return nd[a].id < nd[b].id;}
struct suffixAutomaton
{
int rt;
inline suffixAutomaton() {tp = 1; rt = 0; nd[rt].isReal = 1;}
inline int insert(int lst, int c)
{
int u = tp ++; nd[u].len = nd[lst].len + 1; nd[u].isReal = 1;
for(; ~ lst && nd[lst].suc[c] == -1; lst = nd[lst].pre) nd[lst].suc[c] = u;
if(lst == -1) nd[u].pre = rt;
else
{
int p = nd[lst].suc[c];
if(nd[p].len == nd[lst].len + 1) nd[u].pre = p;
else
{
int q = tp ++; nd[q].len = nd[lst].len + 1; nd[q].pre = nd[p].pre; for(int i = 0; i < 26; ++ i) nd[q].suc[i] = nd[p].suc[i]; //保险起见, 这里不要整个复制
nd[p].pre = nd[u].pre = q;
for(; ~ lst && nd[lst].suc[c] == p; lst = nd[lst].pre) nd[lst].suc[c] = q;
}
}
return u;
}
void build(int u)
{
if(~ nd[u].pre) nd[nd[u].pre].successorOnSuffixTree.push_back(u); nd[u].vst = 1;
for(int i = 0; i < 26; ++ i) if(~ nd[u].suc[i] && ! nd[nd[u].suc[i]].vst) build(nd[u].suc[i]);
}
void getSize(int u)
{
nd[u].sz = 1; nd[u].hvy = -1;
nd[u].dep = ~ nd[u].pre ? nd[nd[u].pre].dep + 1 : 0;
for(auto v : nd[u].successorOnSuffixTree)
{
getSize(v); nd[u].sz += nd[v].sz;
if(nd[u].hvy == -1 || nd[v].sz > nd[nd[u].hvy].sz) nd[u].hvy = v;
}
}
int clk;
void decomposition(int u, int tp)
{
nd[u].L = nd[u].id = clk ++; nd[u].tp = tp; if(nd[u].isReal) seg.addSize(nd[u].id);
if(~ nd[u].hvy) decomposition(nd[u].hvy, tp);
for(auto v : nd[u].successorOnSuffixTree) if(v != nd[u].hvy) decomposition(v, v);
nd[u].R = clk - 1;
}
inline void build() {build(rt); getSize(rt); clk = 0; decomposition(rt, rt);}
inline int getLCA(int u, int v)
{
while(nd[u].tp != nd[v].tp)
{
if(nd[nd[u].tp].dep > nd[nd[v].tp].dep) u = nd[nd[u].tp].pre;
else if(nd[nd[u].tp].dep < nd[nd[v].tp].dep) v = nd[nd[v].tp].pre;
else u = nd[nd[u].tp].pre, v = nd[nd[v].tp].pre;
}
return nd[u].dep < nd[v].dep ? u : v;
}
inline long long query(int u)
{
long long res = 0;
for(; ~ u; u = nd[nd[u].tp].pre) res += seg.query(nd[nd[u].tp].id, nd[u].id);
return res;
}
inline void modify(int *st, int sz)
{
if(! sz) return;
sort(st, st + sz, cmp);
seg.modify(nd[st[0]].L, nd[st[0]].R, 1);
for(int i = 1, p = 0; i < sz; ++ i) if(nd[st[i]].id > nd[st[p]].R)
seg.modify(nd[st[i]].L, nd[st[i]].R, 1), p = i;
}
inline long long query(int *st, int sz)
{
if(! sz) return 0;
sort(st, st + sz, cmp);
long long ans = 0;
ans = query(st[0]);
for(int i = 1; i < sz; ++ i) ans += query(st[i]) - query(getLCA(st[i], st[i - 1]));
return ans;
}
}SAM;
struct trieTree
{
struct node
{
int suc[26], mp;
inline node() {memset(suc, -1, sizeof(suc));}
}nd[N + 1];
inline void addEdge(int u, int c, int v) {nd[u].suc[c] = v; }
inline void buildSuffixAutomaton()
{
deque<int> que; que.clear(); que.push_back(1); nd[1].mp = 0;
for(; ! que.empty(); que.pop_front())
{
int u = que.front();
for(int i = 0; i < 26; ++ i) if(~ nd[u].suc[i]) nd[nd[u].suc[i]].mp = SAM.insert(nd[u].mp, i), que.push_back(nd[u].suc[i]);
}
}
}trie;
int main()
{ #ifndef ONLINE_JUDGE freopen("trie.in", "r", stdin);
freopen("trie.out", "w", stdout); #endif using namespace Zeonfai;
int n = getInt();
for(int i = 2; i <= n; ++ i)
{
int u = getInt(), c = getChar() - 'a';
trie.addEdge(u, c, i);
}
trie.buildSuffixAutomaton();
SAM.build();
int m = getInt();
for(int i = 0; i < m; ++ i)
{
int opt = getInt(), sz = getInt();
static int st[N];
for(int i = 0; i < sz; ++ i) st[i] = trie.nd[getInt()].mp;
if(opt == 1) SAM.modify(st, sz);
if(opt == 2) println(SAM.query(st, sz));
}
}

2016集训测试赛(二十)Problem B: 字典树的更多相关文章

  1. 2016集训测试赛(十九)Problem C: 无聊的字符串

    Solution 傻X题 我的方法是建立后缀后缀树, 然后在DFS序列上直接二分即可. 关键在于如何得到后缀树上每个字符对应的字节点: 我们要在后缀自动机上记录每个点在后缀树上对应的字母. 考虑如何实 ...

  2. 2016集训测试赛(十九)Problem A: 24点大师

    Solution 这到题目有意思. 首先题目描述给我们提供了一种非常管用的模型. 按照题目的方法, 我们可以轻松用暴力解决20+的问题; 关键在于如何构造更大的情况: 我们发现 \[ [(n + n) ...

  3. 2016集训测试赛(十八)Problem C: 集串雷 既分数规划学习笔记

    Solution 分数规划经典题. 话说我怎么老是忘记分数规划怎么做呀... 所以这里就大概写一下分数规划咯: 分数规划解决的是这样一类问题: 有\(a_1, a_2 ... a_n\)和\(b_1, ...

  4. 2016集训测试赛(二十六)Problem A: bar

    Solution 首先审清题意, 这里要求的是子串而不是子序列... 我们考虑用1表示p, -1表示j. 用sum[i]表示字符串前\(i\)的前缀和. 则我们考虑一个字符串\([L, R]\)有什么 ...

  5. 2016集训测试赛(二十四)Problem B: Prz

    Solution 这道题有两个关键点: 如何找到以原串某一个位置为结尾的某个子序列的最晚出现位置 如何找到原串中某个位置之前的所有数字的最晚出现位置中的最大值 第一个关键点: 我们注意到每个数字在\( ...

  6. 2016集训测试赛(二十四)Problem C: 棋盘控制

    Solution 场上的想法(显然是错的)是这样的: 我们假设棋子是一个一个地放置的, 考虑在放置棋子的过程中可能出现哪些状态. 我们令有序整数对\((i, j)\)表示总共控制了\(i\)行\(j\ ...

  7. 2016集训测试赛(二十)Problem A: Y队列

    Solution 考虑给定一个\(n\), 如何求\(1\)到\(n\)的正整数中有多少在队列中. 不难注意到我们只需要处理质数次方的情况即可, 因为合数次方会被其因数处理到. 同时我们考虑到可能存在 ...

  8. 2016集训测试赛(二十一)Problem C: 虫子

    题目大意 给你一棵树, 每个点有一个点权. 有两种操作: link / cut 修改某个点的点权 每次操作后, 你要输出以下答案: 在整棵树中任意选两个点, 这两个点的LCA的期望权值. Soluti ...

  9. 2016北京集训测试赛(十六)Problem C: ball

    Solution 这是一道好题. 考虑球体的体积是怎么计算的: 我们令\(f_k(r)\)表示\(x\)维单位球的体积, 则 \[ f_k(1) = \int_{-1}^1 f_{k - 1}(\sq ...

随机推荐

  1. 基础_String

    String str1="hello"; String str2="hello"; String str3="hello"; String ...

  2. NOI p 2017 TG游记

    嗨小朋友们大家好 还记得我是谁吗 对了我就是为iot配音的演员 弹鸡鸡 今天呐我特别的要向长沙市的oier们 洛谷的oier们 还有cnblogs的oier们问声好 为什么呢 因为我们在2017年11 ...

  3. 极简配置phpstorm+xdebug进行断点调试

    以前调试的时候各种var_dump()就能得到结果,现在入手别人开发的工作,由于不了解业务和代码逻辑,又要去修改bug,就造成了修改bug效率低,所以又拾起来了xdbug,顺便总结了一下phpstor ...

  4. day05_09 列表内置方法

    1.0 count(计算元素出现的次数) t = ['to','be','or','not','to','be'].count('to') print(t) #>>>2 2.0 ex ...

  5. Wordpress 自定义文章类型添加 Categoried、Tags

    默认情况下 ,自定义文章类型没有分类和标签属性,需要通过 register_taxonomy_for_object_type 手动注册文章分类和标签,可以通过在 functions.php 或插件中添 ...

  6. [oldboy-django][6其他]rest framwork有关事

    官网地址: https://github.com/encode/django-rest-framework 英文教程:http://www.django-rest-framework.org/tuto ...

  7. 编程风格---代码中doxygen方式的注释写法

    代码中doxygen方式的注释写法: 1. 模块定义(单独显示一页) /* * @defgroup 模块名 模块的说明文字 * @{ */ … 定义的内容 … /** @} */ // 模块结尾 2. ...

  8. 妹子(girls)

    妹子(girls) 题目描述 万人迷皮皮轩收到了很多妹子的礼物,由于皮皮轩觉得每个妹子都不错,所以将她们礼物的包装盒都好好保存,但长此以往皮皮轩的房间里都堆不下了,所以只能考虑将一些包装盒放进其他包装 ...

  9. 共鸣(resonance)

    共鸣(resonance) 题目描述 GHQ通过在24区引起基因组共鸣,从而引发了第二次失落的圣诞. 24区的地图可以视为一个二维平面.GHQ在24区布置了m架发射塔,而葬仪社也建立了n个据点.要阻止 ...

  10. Codeforces Round #440(Div.2)

    一句话题意: A:给出两个长为\(n\),\(m\)的的数组,每个数在\(1\)到\(9\)之间,求出一个最小的数使得至少有一位出现在一个数组中,且至少有一位出现在另一个数组中.\(n,m\leq9\ ...