2016集训测试赛(二十)Problem B: 字典树

题目大意
你们自己感受一下原题的画风...
我怀疑出题人当年就是语文爆零的
下面复述一下出题人的意思:
- 操作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: 字典树的更多相关文章
- 2016集训测试赛(十九)Problem C: 无聊的字符串
Solution 傻X题 我的方法是建立后缀后缀树, 然后在DFS序列上直接二分即可. 关键在于如何得到后缀树上每个字符对应的字节点: 我们要在后缀自动机上记录每个点在后缀树上对应的字母. 考虑如何实 ...
- 2016集训测试赛(十九)Problem A: 24点大师
Solution 这到题目有意思. 首先题目描述给我们提供了一种非常管用的模型. 按照题目的方法, 我们可以轻松用暴力解决20+的问题; 关键在于如何构造更大的情况: 我们发现 \[ [(n + n) ...
- 2016集训测试赛(十八)Problem C: 集串雷 既分数规划学习笔记
Solution 分数规划经典题. 话说我怎么老是忘记分数规划怎么做呀... 所以这里就大概写一下分数规划咯: 分数规划解决的是这样一类问题: 有\(a_1, a_2 ... a_n\)和\(b_1, ...
- 2016集训测试赛(二十六)Problem A: bar
Solution 首先审清题意, 这里要求的是子串而不是子序列... 我们考虑用1表示p, -1表示j. 用sum[i]表示字符串前\(i\)的前缀和. 则我们考虑一个字符串\([L, R]\)有什么 ...
- 2016集训测试赛(二十四)Problem B: Prz
Solution 这道题有两个关键点: 如何找到以原串某一个位置为结尾的某个子序列的最晚出现位置 如何找到原串中某个位置之前的所有数字的最晚出现位置中的最大值 第一个关键点: 我们注意到每个数字在\( ...
- 2016集训测试赛(二十四)Problem C: 棋盘控制
Solution 场上的想法(显然是错的)是这样的: 我们假设棋子是一个一个地放置的, 考虑在放置棋子的过程中可能出现哪些状态. 我们令有序整数对\((i, j)\)表示总共控制了\(i\)行\(j\ ...
- 2016集训测试赛(二十)Problem A: Y队列
Solution 考虑给定一个\(n\), 如何求\(1\)到\(n\)的正整数中有多少在队列中. 不难注意到我们只需要处理质数次方的情况即可, 因为合数次方会被其因数处理到. 同时我们考虑到可能存在 ...
- 2016集训测试赛(二十一)Problem C: 虫子
题目大意 给你一棵树, 每个点有一个点权. 有两种操作: link / cut 修改某个点的点权 每次操作后, 你要输出以下答案: 在整棵树中任意选两个点, 这两个点的LCA的期望权值. Soluti ...
- 2016北京集训测试赛(十六)Problem C: ball
Solution 这是一道好题. 考虑球体的体积是怎么计算的: 我们令\(f_k(r)\)表示\(x\)维单位球的体积, 则 \[ f_k(1) = \int_{-1}^1 f_{k - 1}(\sq ...
随机推荐
- C#+VisionPro连接相机获取图像的两种方式
两种比较常用的方式. C#直接连接相机获取图像(GIGE) 在获取图像前,需要先创建一个相机对象,再使用这个相机对象的Acquire方法拍摄照片. ICogAcqFifo macqfifo;//定义相 ...
- “帮你APP”团队冲刺4
1.整个项目预期的任务量 (任务量 = 所有工作的预期时间)和 目前已经花的时间 (所有记录的 ‘已经花费的时间’),还剩余的时间(所有工作的 ‘剩余时间’) : 所有工作的预期时间:88h 目前已经 ...
- 项目管理者必知:适用于仪表盘项目的7个优秀JavaScript库
仪表盘是用于目标或业务流程的视觉指示工具,也用于切割杂乱无章的数据,从而分割出要点的重要工具.它可以帮助评估信息并及时做出正确的决定,一款实时可视化的仪表盘通常由图标.测绘图.图形符号以及数据表格等组 ...
- 设计模式之第1章-工厂方法模式(Java实现)
设计模式之第1章-工厂方法模式(Java实现) “我先来”,“不,老公,我先!”.远远的就听到几个人,哦不,是工厂方法模式和抽象工厂模式俩小夫妻在争吵,尼妹,又不是吃东西,谁先来不都一样(吃货的世界~ ...
- 36、imageview的坑
当频繁设置imageview的背景图片时,用: imageviewChooseStaff.setImageResource(R.drawable.default_head_pic); 而不是 imag ...
- 【palindrome partitioning II】cpp
题目: Given a string s, partition s such that every substring of the partition is a palindrome. Return ...
- sqlserver 操作access数据库
exec sp_configure 'show advanced options',1 reconfigure exec sp_configure 'Ad Hoc Distributed Quer ...
- [oldboy-django][3作业汇总]登录,注册最终版
# 作业(登录,注册)最终版 - 保留上次输入的值 - 用户数据格式的验证
- php 备份数据库代码(生成word,excel,json,xml,sql)
单表备份代码: 复制代码代码如下: <?php class Db { var $conn; function Db($host="localhost",$user=" ...
- Ubuntu系统设置中心不见了
sudo apt-get install unity-control-center 执行安装上述即可 原因是因为在安装搜狗输入法的时候卸载了ibuts与系统控制中心