题目大意
你们自己感受一下原题的画风...
我怀疑出题人当年就是语文爆零的
下面复述一下出题人的意思:
- 操作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('
');
}
}
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));
}
}