题目大意

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

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

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

  • 操作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. 常用Style

    有些输入框什么的,字数限制什么的style,ceb为我们写好了.我感觉,每个app的style都是很有用的一个东西. <?xml version="1.0" encoding ...

  2. 洛谷P1424小鱼的航程改进版

    题目链接https://www.luogu.org/problemnew/show/P1424

  3. 3 - JVM随笔分类(gc.log ,VisualVM插件介绍,VisualVM远程连接方式介绍)

    gc.log 354.2 KB 对于对应用的监控上可以使用Jdk自带的VisualVM来做可视化监控,可以查看当前服务应用进程的堆大小的走向,以及类的加载数量等,除此之外,VisualVM可以支持很多 ...

  4. loj2091 「ZJOI2016」小星星

    ref 总的来说,就是 容斥转化为点对应到点集问题. 树形 dp 解决转化后的问题. #include <iostream> #include <cstring> #inclu ...

  5. Python框架之Django学习笔记(六)

    模板 上篇博文学习了动态视图,但是,视图中返回文本的方式有点特别. 也就是说,HTML被直接硬编码在 Python 代码之中. def current_datetime(request): now = ...

  6. 9.Iptables与Firewalld防火墙

    第9章 Iptables与Firewalld防火墙 章节简述: 保障数据的安全性是继保障数据的可用性之后最为重要的一项工作.防火墙作为公网与内网之间的保护屏障,在保障数据的安全性方面起着至关重要的作用 ...

  7. Python+Selenium基础篇之5-第一个完整的自动化测试脚本

    前面文章,我们介绍了如何采用XPath表达式去定位网页元素,在掌握了如何抓取或者如何书写精确的XPath表达式后,我们可以开始写自己的第一个真正意义上的webui 自动化测试脚本,就相当于,你在学习P ...

  8. idea热部署设置(复制)

    提出问题 IntelliJ IDEA工具如何设置热部署??? 解决问题 我的IDEA的版本是:IntelliJ IDEA 14.0.2 第一步:打开tomcat配置 这里写图片描述 第二步: 这里写图 ...

  9. asp.net允许跨域访问

    C# ASP.NET MVC 配置允许跨域访问 在web.config文件中的 system.webServer 节点下 增加如下配置 <httpProtocol> <customH ...

  10. BZOJ 2818: Gcd(欧拉函数)

    GCDDescription 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数对(x,y)有多少对. Input 一个整数N Output 如题 Sample Input 4 ...