题目大意

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

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

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

  • 操作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. git上传自己的代码

    感谢这个哥们的博客,不过里面有些错误. http://www.cnblogs.com/ruofengzhishang/p/3842587.html 下面是我自己的实践成功的: 这篇文章写得是windo ...

  2. jq相关细节-1

    animate/动画 所有用于动画的属性必须是数字的,除非另有说明:这些属性如果不是数字的将不能使用基本的jQuery功能.(例如,width, height或者left可以执行动画,但是backgr ...

  3. R语言分析朝阳医院数据

    R语言分析朝阳医院数据 本次实践通过分析朝阳医院2016年销售数据,得出“月均消费次数”.“月均消费金额”.“客单价”.“消费趋势”等结果,并据此作出可视化图形. 一.读取数据: library(op ...

  4. 4、CSS基础part-2

    1.background-1 ①设置background-image ②设置background-attachment为fixed 可以声明图像相对于可视区是固定的(fixed),因此不会受到滚动的影 ...

  5. Jmeter随笔一

    资料分享:http://www.cnblogs.com/yangxia-test/p/3964881.html

  6. PAT1017

    本题要求计算A/B,其中A是不超过1000位的正整数,B是1位正整数.你需要输出商数Q和余数R,使得A = B * Q + R成立. 输入格式: 输入在1行中依次给出A和B,中间以1空格分隔. 输出格 ...

  7. POJ 1364:King(差分约束)

    题目大意:判断是否存在一个长度为n的序列满足给出的不等关系. 分析: 将序列和转化成用两个前缀和之差来表示即可变为差分约束系统. 需要注意的是不能忘记n要加+1,因为还有一个特殊源点,自己因为n:=n ...

  8. Unity3D 绘制扇形/环形

    using UnityEngine; using System.Collections; using System.Collections.Generic; public class Cone : M ...

  9. Codeforces Round #323 (Div. 2) A 水

    A. Asphalting Roads time limit per test 1 second memory limit per test 256 megabytes input standard ...

  10. vim 编辑器的使用

    相信一个linux运维人员不可能不知道vim ,下面我们一起来学习vim的日常操作吧.(不要追求多,工作中用到了再去学也不迟.) 1.vim 的几种模式 *正常模式:快捷键or命令行操作 *插入模式: ...