各种OJ刷题记录6.27-7.6

来源:互联网 发布:大漠驼铃 php 编辑:程序博客网 时间:2024/05/17 01:00

各种OJ刷题记录6.27-7.6


6175. 「美团 CodeM 初赛 Round B」黑白树

之前一道题的增强版…

考虑所有ki都相等,则可以从深度由深到浅贪心,如果还没有被覆盖就从这个节点覆盖上去。

但对于ki不同是有反例的:

http://blog.csdn.net/mengxiang000000/article/details/73718867

考虑先用一边dfs记录子树中覆盖当前节点最浅到哪里,计为low_dep[nd]。考虑到一个未被覆盖的节点nd时,覆盖掉low_dep[nd]..nd中所有的节点,并cnt++即可。

没太看懂神犇们的高端写法…只好大力树剖+树状数组维护了。

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct node {    int to, next;} edge[MAXN];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int siz[MAXN], id[MAXN], ind[MAXN], id_in_ds = 0;int c[MAXN];int n;int ki[MAXN];int depth[MAXN], fa[MAXN][20];int low_dep[MAXN];int dfn[MAXN], dfn_top = 0;inline int lowbit(int i){ return i&(-i); }void modify(int i, int dt){    for (; i <= n; i += lowbit(i))        c[i] += dt;}void cover(int i, int j){ modify(i, 1), modify(j+1, -1); }int dat(int i){    int ans = 0;    for (; i; i -= lowbit(i))        ans += c[i];    return ans;}int kth_ans(int nd, int k){    if (k >= depth[nd]) k = depth[nd];    for (register int i = 0; i < 20; i++)        if (k&(1<<i))            nd = fa[nd][i];    return nd;}void dfs_init(int nd){    siz[nd] = 1;    for (register int i = 1; i < 20; i++)        fa[nd][i] = fa[fa[nd][i-1]][i-1];    low_dep[nd] = kth_ans(nd, ki[nd]-1);    for (int i = head[nd]; i; i = edge[i].next) {        depth[edge[i].to] = depth[nd]+1;        dfs_init(edge[i].to), siz[nd] += siz[edge[i].to];        if (depth[low_dep[edge[i].to]] < depth[low_dep[nd]])            low_dep[nd] = low_dep[edge[i].to];    }    dfn[++dfn_top] = nd;}inline bool cmp(int a, int b){ return depth[a] > depth[b]; }void dfs_build(int nd, int from){    id[nd] = ++id_in_ds, ind[nd] = from;    int hev = -1;    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (hev == -1 || siz[to] > siz[hev]) hev = to;    }    if (hev == -1) return;    dfs_build(hev, from);    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to != hev) dfs_build(to, to);    }}void cover_point(int nd, int pt){    while (depth[ind[nd]] > depth[pt]) {        cover(id[ind[nd]], id[nd]);        nd = fa[ind[nd]][0];    }    if (depth[nd] >= depth[pt]) cover(id[pt], id[nd]);}int main(){    scanf("%d", &n);    for (int i = 2; i <= n; i++)         scanf("%d", &fa[i][0]), push(fa[i][0], i);    for (int i = 1; i <= n; i++) scanf("%d", &ki[i]);    dfs_init(1);    dfs_build(1, 1);    int cnt = 0;    for (int i = 1; i <= n; i++) {        int pt = dfn[i];        if (dat(id[pt]) != 0) continue;        int anc = low_dep[pt];        cover_point(pt, anc), cnt++;    }    printf("%d\n", cnt);    return 0;}

loj#112. 三维偏序

填坑…

不知道为什么印象特别深刻去年一次NOIP模拟赛建出二维数点模型,然后Too Naive,并不会做…一直向转化成逆序对…学习了一个偏序那套理论才知道Too Simple..

经典题,一维排序,二维归并,三维树状数组。

注意check一下相同的情况。

#include <bits/stdc++.h>using namespace std;inline int read(){    int a = 0, c;    do c = getchar(); while(!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}const int MAXN = 100005;struct pt {    int x, y, z, id;} pts[MAXN], tmp[MAXN];int n, k;inline bool cmp_x(const pt &a, const pt &b){ return a.x < b.x; }inline bool cmp_y(const pt &a, const pt &b){ return a.y < b.y; }int ans[MAXN];int c[MAXN*2];inline int lowbit(int i){ return i&(-i); }void modify(int i, int dt){    for (; i <= k; i += lowbit(i))        c[i] += dt;}int sum(int i){    int ans = 0;    for (; i; i -= lowbit(i))        ans += c[i];    return ans;}void solve(int l, int r, int L, int R){    if (l >= r) return;    L = pts[l].x, R = pts[r].x;    if (L == R) {        sort(pts+l, pts+r+1, cmp_y);        int lp = l, rp = l;        while (lp <= r) {            while (rp+1 <= r && pts[rp+1].y == pts[lp].y) rp++;            for (int i = lp; i <= rp; i++)                     modify(pts[i].z, 1);            for (int i = lp; i <= rp; i++)                ans[pts[i].id] += sum(pts[i].z)-1;            lp = rp = rp+1;        }        for (int i = l; i <= r; i++) modify(pts[i].z, -1);        return;    }    int MID = (L+R)>>1;    int lp = l, rp = r;    for (int i = l; i <= r; i++) {        if (pts[i].x <= MID) tmp[lp++] = pts[i];        else tmp[rp--] = pts[i];    }    int mid = lp-1;    for (int i = l; i <= r; i++)        pts[i] = tmp[i];    solve(l, mid, L, MID), solve(mid+1, r, MID+1, R);    lp = l, rp = mid+1;    int tmp_l = l;    while (lp <= mid && rp <= r) {        if (pts[lp].y <= pts[rp].y) {            modify(pts[lp].z, 1);            tmp[tmp_l++] = pts[lp++];        } else {            ans[pts[rp].id] += sum(pts[rp].z);            tmp[tmp_l++] = pts[rp++];        }    }    while (lp <= mid) {        modify(pts[lp].z, 1);        tmp[tmp_l++] = pts[lp++];    }    while (rp <= r) {        ans[pts[rp].id] += sum(pts[rp].z);        tmp[tmp_l++] = pts[rp++];    }    for (int i = l; i <= mid; i++) modify(pts[i].z, -1);    for (int i = l; i <= r; i++) pts[i] = tmp[i];}int d[MAXN];int main(){    scanf("%d%d", &n, &k);    for (int i = 1; i <= n; i++)        pts[i].x = read(), pts[i].y = read(), pts[i].z = read(), pts[i].id = i;    sort(pts+1, pts+n+1, cmp_x);    solve(1, n, 1, k);    for (int i = 1; i <= n; i++)        d[ans[i]]++;    for (int i = 0; i < n; i++)        printf("%d\n", d[i]);    return 0;}

NOIP2016 天天爱跑步

填一下这个坑….

考虑向上的路径ST,一个观测点j观测到的条件是:

depth[S]depth[j]=W[j],j{ST}

也就是depth[S]=depth[j]+W[j]。因而一个向上的路径的贡献就是其路径上depth+W为定值的点。可以在S打一个(depth[S],1)标记,T打一个(depth[S],1)标记。反过来看,对于一个观测点,查询的其实就是子树中所有(depth[j]+W[j],1)标记个数减去(depth[j]+W[j],1)标记个数。这可以用dfs序+可持久化数组来维护,后者可以使用可持久化线段树或者可持久化平衡树实现(rope也可以),复杂度O(nlgn),理论能拿到95分,然而在Cogs只80…后面还是RE…

#include <bits/stdc++.h>#include <ext/rope>using namespace __gnu_cxx;using namespace std;const int MAXN = 300005;struct node {    int to, next;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int depth[MAXN], W[MAXN];int u[MAXN*2], v[MAXN*2], id[MAXN*2];int n, m;int lca[MAXN], vis[MAXN];int fa[MAXN], father[MAXN], ans[MAXN];inline int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }inline int read(){    int a = 0, c;    do c = getchar(); while(!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}vector<int> pt[MAXN];struct tag {    int dat;    int tp;};vector<tag> tag_up[MAXN], tag_down[MAXN];void dfs_init(int nd, int f){    vis[nd] = 1, father[nd] = f;    for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)        if (vis[v[*i]]) {            lca[id[*i]] = findf(v[*i]);        }    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f) continue;        depth[to] = depth[nd]+1, dfs_init(to, nd);    }    fa[nd] = f;}void put_tag(){    for (int i = 1; i <= m; i++) {        int c = lca[i], s = u[i], t = v[i];        if (c == t) {            tag_up[s].push_back((tag){depth[s], 1});            tag_up[father[c]].push_back((tag){depth[s], -1});        } else if (c == s) {            tag_down[t].push_back((tag){-depth[s], 1});            tag_down[father[c]].push_back((tag){-depth[s], -1});        } else {            tag_up[s].push_back((tag){depth[s], 1});            tag_up[father[c]].push_back((tag){depth[s], -1});            int beg = depth[s]-depth[c];            tag_down[t].push_back((tag){beg-depth[c], 1});            tag_down[c].push_back((tag){beg-depth[c], -1});        }    }}int dfn[MAXN], dfn_top = 0, out[MAXN];struct my_array {    rope<int> *arr[MAXN];    int range_l, range_r, lev;    void build()    {        arr[0] = new rope<int>;        lev = 0;        for (int i = range_l; i <= range_r; i++)            arr[0]->push_back(0);    }    inline void new_lev()    { ++lev, arr[lev] = new rope<int>(*arr[lev-1]); }    inline void add_pos(int pos, int dt)    {        pos = pos-range_l;        int dat = arr[lev]->at(pos)+dt;        arr[lev]->replace(pos, dat);    }    inline int segment_sum(int l, int r, int pos)    { return arr[r]->at(pos-range_l)-arr[l-1]->at(pos-range_l); }} arr_up, arr_down;void dfs_travel(int nd){    dfn[nd] = ++dfn_top;    arr_up.new_lev(), arr_down.new_lev();    for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)         arr_up.add_pos(i->dat, i->tp);    for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)        arr_down.add_pos(i->dat, i->tp);    for (register int i = head[nd]; i; i = edge[i].next)         if (edge[i].to != father[nd]) dfs_travel(edge[i].to);    out[nd] = dfn_top;}int main(){    freopen("runninga.in", "r", stdin);    freopen("runninga.out", "w", stdout);    int size = 128 << 20;    char *p = (char*)malloc(size) + size;      __asm__("movl %0, %%esp\n" :: "r"(p));    n = read(), m = read();    for (register int i = 1; i < n; i++) {        int x = read(), y = read();        push(x, y), push(y, x);    }    for (register int i = 1; i <= n; i++) W[i] = read();    for (register int i = 1; i <= m; i++) {        u[i] = read(), v[i] = read();        u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;        pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);    }    dfs_init(1, 0);    put_tag();    arr_up.range_l = 0, arr_up.range_r = 2*n+1, arr_down.range_l = -n-1, arr_down.range_r = n+1;    arr_up.build(), arr_down.build();    dfs_travel(1);    for (register int i = 1; i <= n; i++) {        ans[i] += arr_up.segment_sum(dfn[i], out[i], depth[i]+W[i]);        ans[i] += arr_down.segment_sum(dfn[i], out[i], W[i]-depth[i]);    }    for (register int i = 1; i <= n; i++)        printf("%d ", ans[i]);    puts("");    return 0;}

然而这题就是典型“会的越多得分越低”型题目…这个可持久化维护的特别鸡肋。由于答案具有可减性,我们在dfs的过程中维护一个数组,进入时记录一下答案,退出时记录一下,差值就是所求了…

#include <bits/stdc++.h>#include <ext/rope>using namespace __gnu_cxx;using namespace std;const int MAXN = 300005;struct node {    int to, next;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int depth[MAXN], W[MAXN];int u[MAXN*2], v[MAXN*2], id[MAXN*2];int n, m;int lca[MAXN], vis[MAXN];int fa[MAXN], father[MAXN], ans[MAXN];inline int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }inline int read(){    int a = 0, c;    do c = getchar(); while(!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}vector<int> pt[MAXN];struct tag {    int dat;    int tp;};vector<tag> tag_up[MAXN], tag_down[MAXN];void dfs_init(int nd, int f){    vis[nd] = 1, father[nd] = f;    for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)        if (vis[v[*i]]) {            //cerr << *i << " " << id[*i] << " " << v[*i] << endl;            lca[id[*i]] = findf(v[*i]);        }    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f) continue;        depth[to] = depth[nd]+1, dfs_init(to, nd);    }    fa[nd] = f;}void put_tag(){    for (int i = 1; i <= m; i++) {        int c = lca[i], s = u[i], t = v[i];        // cerr << "LCA(" << s << "," << t << ") = " << c << endl;        if (c == t) {            tag_up[s].push_back((tag){depth[s], 1});            tag_up[father[c]].push_back((tag){depth[s], -1});        } else if (c == s) {            tag_down[t].push_back((tag){-depth[s], 1});            tag_down[father[c]].push_back((tag){-depth[s], -1});        } else {            tag_up[s].push_back((tag){depth[s], 1});            tag_up[father[c]].push_back((tag){depth[s], -1});            int beg = depth[s]-depth[c];            tag_down[t].push_back((tag){beg-depth[c], 1});            tag_down[c].push_back((tag){beg-depth[c], -1});        }    }}int arr_up[MAXN*4], arr_down[MAXN*4], beg = 600000;void dfs_travel(int nd){    int pre = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg];    for (register int i = head[nd]; i; i = edge[i].next)         if (edge[i].to != father[nd]) dfs_travel(edge[i].to);    for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)         arr_up[i->dat+beg] += i->tp;    for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)        arr_down[i->dat+beg] += i->tp;    ans[nd] = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg]-pre;}int main(){    n = read(), m = read();    for (register int i = 1; i < n; i++) {        int x = read(), y = read();        push(x, y), push(y, x);    }    for (register int i = 1; i <= n; i++) W[i] = read();    for (register int i = 1; i <= m; i++) {        u[i] = read(), v[i] = read();        u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;        pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);    }    dfs_init(1, 0);    put_tag();    dfs_travel(1);    for (register int i = 1; i <= n; i++)        printf("%d ", ans[i]);    puts("");    return 0;}

NOIP2015 运输计划

继续填古代坑计划…

当时用SPFA水了25…

现在看来很水啊..二分后随便树剖就行了。也可以树上差分搞。

#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;struct node {    int to, next, dis;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j, int d){ edge[++top] = (node) {j, head[i], d}, head[i] = top; }int fa[MAXN];inline int findf(int a){ return fa[a]?fa[a] = findf(fa[a]):a; }int u[MAXN*2], v[MAXN*2], id[MAXN*2], lca[MAXN], vis[MAXN], father[MAXN];int dis[MAXN];int n, m;vector<int> pt[MAXN];int depth[MAXN], len[MAXN];inline int read(){    int a = 0, c;    do c = getchar(); while (!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}void dfs_init(int nd, int f){    vis[nd] = 1, father[nd] = f;    for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)        if (vis[v[*i]])            lca[id[*i]] = findf(v[*i]);    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == f) continue;        depth[to] = depth[nd]+1, len[to] = len[nd]+edge[i].dis;        dfs_init(to, nd);    }    fa[nd] = f;}int tag[MAXN], max_able = 0;void dfs_collect(int nd){    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to == father[nd]) continue;        dfs_collect(to), tag[nd] += tag[to];    }}bool check(int ans){    memset(tag, 0, sizeof tag);    int max_val = 0, cnt = 0;    max_able = 0;    for (int i = 1; i <= m; i++)        if (dis[i] > ans) {            max_val = max(max_val, dis[i]);            cnt++;            // cerr << u[i] << " " << v[i] << " " << lca[i] << endl;            tag[u[i]]++, tag[v[i]]++, tag[father[lca[i]]]--, tag[lca[i]]--;        }    if (cnt == 0) return 1;    //cerr << cnt << endl;    dfs_collect(1);        for (int i = 1; i <= n; i++) {        if (tag[i] != cnt) continue;        //cerr << tag[i] << endl;        for (int j = head[i]; j; j = edge[j].next) {            int to = edge[j].to;            // if (to == father[i]) continue;            if (tag[to] == cnt)                max_able = max(max_able, edge[j].dis);        }    }    //cerr << max_val << " " << max_able << " " << ans << endl;    return max_val-max_able <= ans; }int main(){    //freopen("transport.in", "r", stdin);    //freopen("transport.out", "w", stdout);    n = read(), m = read();    for (int i = 1; i < n; i++) {        int l = read(), r = read(), d = read();        push(l, r, d), push(r, l, d);    }    for (int i = 1; i <= m; i++) {        u[i] = read(), v[i] = read();        v[i+m] = u[i], u[i+m] = v[i], id[i] = id[i+m] = i;        pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);    }    dfs_init(1, 0);    for (int i = 1; i <= m; i++)        dis[i] = len[u[i]]+len[v[i]]-len[lca[i]]*2;    int l = 0, r = 300000ll*1000, mid;    while (l <= r) {        mid = (l+r)>>1;        if (check(mid)) r = mid-1;        else l = mid+1;    }    printf("%d\n", r+1);    return 0;}

loj#6066. 「2017 山东一轮集训 Day3」第二题

我SX….原来括号序列就是dfs序啊..

二分答案k,维护括号序列hash,考虑对于节点nd,以其为根的子树恰好被删去即是在其k+1个祖先处,打一个标记,然后对于每个节点大力计算就好了。复杂度O(nlg2n),如果k祖先用长链剖分、排序用基数排序,可以做到O(nlgn)

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct node {    int to, next;} edge[MAXN];int head[MAXN], top = 0;void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int hash_dat[MAXN*2], dfn_top = 0;vector<pair<int, int> > forbidden[MAXN];int fa[MAXN][20], n;int dfn_in[MAXN], dfn_out[MAXN];typedef unsigned long long ull;ull hash_tab[MAXN*2], bs = 19260817, power[MAXN*2];int height[MAXN];// --- hash ---void hash_init(){    power[0] = 1;    for (int i = 1; i <= n*2; i++) power[i] = power[i-1]*bs;    for (int i = 1; i <= n*2; i++)        hash_tab[i] = hash_tab[i-1]*bs+hash_dat[i];}ull hash_val(int l, int r){ return hash_tab[r]-hash_tab[l-1]*power[r-l+1]; }// --- hash ---int kth_anc(int nd, int k){    for (int i = 0; i < 20; i++)        if (k&(1<<i))            nd = fa[nd][i];    return nd;}void dfs_init(int nd){    height[nd] = 0;    ++dfn_top, hash_dat[dfn_top] = 1;    dfn_in[nd] = dfn_top;    for (int i = 1; i < 20; i++)        fa[nd][i] = fa[fa[nd][i-1]][i-1];    for (int i = head[nd]; i; i = edge[i].next) {        fa[edge[i].to][0] = nd, dfs_init(edge[i].to);        height[nd] = max(height[nd], height[edge[i].to]+1);    }    ++dfn_top, hash_dat[dfn_top] = 2;    dfn_out[nd] = dfn_top;}set<ull> st;bool calc(int k){    for (int i = 1; i <= n; i++) forbidden[i].clear();    st.clear();    for (int i = 1; i <= n; i++) {        int nd = kth_anc(i, k+1);        forbidden[nd].push_back(make_pair(dfn_in[i], dfn_out[i]));    }    for (int i = 1; i <= n; i++) {        sort(forbidden[i].begin(), forbidden[i].end());        forbidden[i].push_back(make_pair(dfn_out[i]+1, dfn_out[i]+1));    }    for (int i = 1; i <= n; i++) {        if (height[i] < k) continue;        ull hash_v = 0;        int l = dfn_in[i];        for (int j = 0, sz = forbidden[i].size(); j < sz; j++) {            int r = forbidden[i][j].first-1;            if (l <= r)                 hash_v = hash_v*power[r-l+1]+hash_val(l, r);            l = forbidden[i][j].second+1;        }        if (st.count(hash_v)) return 1;        st.insert(hash_v);    }    return 0;}inline int read(){    int a = 0, c;    do c = getchar(); while (!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}int rd[MAXN], rt;int main(){    n = read();    for (int i = 1; i <= n; i++) {        int x, v;        x = read();        for (int j = 1; j <= x; j++)            v = read(), push(i, v), rd[v]++;    }    for (int i = 1; i <= n; i++)        if (rd[i] == 0) {            rt = i;            break;        }    dfs_init(rt), hash_init();    int l = 0, r = n, mid;    while (l <= r) {        mid = (l+r)>>1;        if (calc(mid)) l = mid+1;        else r = mid-1;    }    printf("%d\n", l-1);    return 0;}

loj #6173. Samjia和矩阵

这个命题思路在哪里见过….当时是kmp,现在用SAM就好了。

考虑枚举矩形的高度i,则高度为i的矩形可以看成以i维向量Si为字符集的长度为m的字符串。

人话就是,把一列看成一个字符。

然后就可以用广义后缀自动机大法了。由于字符集太大,需要hash+map<ull>

ps : std貌似是后缀数组,复杂度少一个log|S|,速度快到不知哪里去了…

#include <bits/stdc++.h>using namespace std;typedef unsigned long long ull;int n, m;char A[115][115];const int MAXN = 30005;const ull bas = 19260817;struct SAM {    map<ull,int> chl[MAXN];    int fa[MAXN], maxl[MAXN], top, root;    void clear()    { top = root = 1, maxl[1] = 0; }    int push(int nd, ull dat)    {        int p = nd, np = ++top; maxl[np] = maxl[p]+1, chl[np].clear();        while (p && !chl[p][dat]) chl[p][dat] = np, p = fa[p];        if (!p) fa[np] = root;        else {            int q = chl[p][dat];            if (maxl[q] == maxl[p]+1) fa[np] = q;            else {                int nq = ++top; chl[nq] = chl[q], maxl[nq] = maxl[p]+1;                fa[nq] = fa[q], fa[q] = fa[np] = nq;                while (p && chl[p][dat] == q) chl[p][dat] = nq, p = fa[p];            }        }        return np;    }} sam_main;struct trie {    map<ull, int> tree[MAXN];    int top, root;    trie()    { top = root = 1; }    void clear()    {        for (int i = 1; i <= top; i++) tree[i].clear();        top = root = 1;    }    void push(vector<ull> &str)    {        int nd = root;        for (auto i : str) {            if (!tree[nd].count(i)) tree[nd][i] = ++top;            nd = tree[nd][i];        }    }} trie_main;queue<int> que;int st[MAXN];void bfs_build_sam(){    sam_main.clear();    que.push(trie_main.root);    st[trie_main.root] = sam_main.root;    while (!que.empty()) {        int nd = que.front(); que.pop();        for (auto i : trie_main.tree[nd]) {            st[i.second] = sam_main.push(st[nd], i.first);            que.push(i.second);        }    }}int solve(){    bfs_build_sam();    int ans = 0;    for (int i = 2; i <= sam_main.top; i++)        ans += sam_main.maxl[i]-sam_main.maxl[sam_main.fa[i]];    return ans;}ull hash_val[120][120]; // i, j 第i列前j项哈希ull power[120];inline ull hash_seg(int i, int l, int r){ return hash_val[i][r]-hash_val[i][l-1]*power[r-l+1]; }int main(){    scanf("%d%d", &n, &m);    power[0] = 1;    for (int i = 1; i <= n; i++) power[i] = power[i-1]*bas;    for (int i = 1; i <= n; i++)        scanf("%s", A[i]+1);    for (int i = 1; i <= m; i++) {        for (int j = 1; j <= n; j++)            hash_val[i][j] = hash_val[i][j-1]*bas+A[j][i]-'A'+1;    }    vector<ull> vec;    int ans = 0;    for (int i = 1; i <= n; i++) {        trie_main.clear();        for (int j = 1; j+i-1 <= n; j++) {            vec.clear();            for (int k = 1; k <= m; k++)                vec.push_back(hash_seg(k, j, j+i-1));            trie_main.push(vec);        }        ans += solve();    }    cout << ans << endl;    return 0;}

loj#2098. 「CQOI2015」多项式

看不懂神犇五行代码系列qwq…

考虑展开右边级数第n,n1...项,发现:

bn=anbn1bn(n1)t=an1bn2bn1(n11)t+bn(n2)t2=an2

然后就py直接做了…

import mathdef choose(n, k):    ans = 1    for i in range(k):        ans = ans*(n-i)    for i in range(1, k+1):        ans = ans/i    return ansn = int(raw_input())t = int(raw_input())m = int(raw_input())def mul(a, b):    c = {}    for i in range(2):        for j in range(2):            c[i, j] = 0            for k in range(2):                c[i, j] = (c[i, j]+a[i, k]*b[k, j])%3389    return cdef power(a, n):    ans = {(0,0):1, (1,1):1, (1, 0):0, (0,1):0}    for i in range(10000):        if (((n>>i)&1) != 0):            ans = mul(ans, a)        a = mul(a, a)    return ansdef get_A(n):    c = {}    c[0, 0], c[0, 1], c[1, 0], c[1, 1] = 1234, 5678, 0, 1    c = power(c, n)    return (c[0, 0]+c[0, 1])%3389b = {}b[0] = get_A(n)for i in range(1, 6):    tmp, flag, tt = n-i, 1, t    b[i] = get_A(n-i)    for j in range(1, i+1):        b[i] = b[i]+b[i-j]*choose(tmp+j, j)*t**j*flag        flag, tt = -flag, tt*t    # print(b[i])print(b[n-m])

「NOI2016」优秀的拆分

补题计划continue…

首先考场是肯定写85-95的…后面的性价比不高..

正解其实也(看过题解后)比较简单。考虑求AABB的个数只要求出形如AA的结尾位置tag_after[]和开头位置tag_bef[],然后tag_after[i]+tag_bef[i+1]即为所求。考虑枚举AA的长度l,并将l,2l,作为关键点。对于一个方案可以转化成求两个关键点lcplcs。如果lcp+lcsl,说明存在一个拆分,在对应的位置区间加1。求lcp和lcs可以用后缀数组或者二分哈希(单哈希会被卡..双哈希在uoj貌似TLE…但loj和bzoj过了)。

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;typedef unsigned long long ull;char str[MAXN];int n, T;ull hash_tab[MAXN], bas = 31;long long hash_tab2[MAXN], bas2 = 97, mod = 1e9+7;ull power[MAXN];long long power2[MAXN];void hash_init(){    for (register int i = 1; i <= n+n; i++)        hash_tab[i] = hash_tab[i-1]*bas+str[i]-'a'+1;    for (register int i = 1; i <= n+n; i++)        hash_tab2[i] = (hash_tab2[i-1]*bas2%mod+str[i]-'a'+1)%mod;}inline ull hash_val1(int l, int r){ return l <= r ? hash_tab[r]-hash_tab[l-1]*power[r-l+1] : 0; }inline ull hash_val2(int l, int r){ return l <= r ? ((hash_tab2[r]-hash_tab2[l-1]*power2[r-l+1]%mod)%mod+mod)%mod : 0; }int tag_after[MAXN], tag_bef[MAXN];int pos[MAXN], top = 0;int max_len_lr(int a, int b, int upp){    register int l = 0, r = upp, mid;    while (l <= r) {        mid = (l+r)>>1;        if (hash_val1(a, a+mid-1) == hash_val1(b, b+mid-1)            && hash_val2(a, a+mid-1) == hash_val2(b, b+mid-1)) l = mid+1;        else r = mid-1;    }    return l-1;}int max_len_rl(int a, int b, int upp){    register int l = 0, r = upp, mid;    while (l <= r) {        mid = (l+r)>>1;        if (hash_val1(a-mid+1, a) == hash_val1(b-mid+1, b)            && hash_val2(a-mid+1, a) == hash_val2(b-mid+1, b)) l = mid+1;        else r = mid-1;    }    return l-1;}int main(){    scanf("%d", &T);    while (T--) {        scanf("%s", str+1);        memset(tag_bef, 0, sizeof tag_bef);        memset(tag_after, 0, sizeof tag_after);        n = strlen(str+1);        power[0] = power2[0] = 1;        for (register int i = 1; i <= n; i++) power[i] = power[i-1]*bas;        for (register int i = 1; i <= n; i++) power2[i] = power2[i-1]*bas2%mod;        for (register int i = n+1; i <= n+n; i++) str[i] = int('z')+1;        hash_init();        for (register int i = 1; i <= n; i++) {            top = 0;            do {                ++top, pos[top] = pos[top-1]+i;            } while (pos[top] < n);            register int a, b, lr, rl;            for (register int j = 1; j < top; j++) {                a = pos[j], b = pos[j+1];                lr = max_len_lr(a, b, i), rl = max_len_rl(a, b, i);                if (lr+rl-1 < i) continue;                tag_after[b+i-rl]++, tag_after[b+lr]--;                tag_bef[a-(i-lr)+1]--, tag_bef[a-rl+1]++;            }        }        for (register int i = 1; i <= n+n; i++) tag_after[i] += tag_after[i-1], tag_bef[i] += tag_bef[i-1];        long long ans = 0;        for (register int i = 1; i <= n; i++)             ans += (long long)tag_after[i]*tag_bef[i+1];        printf("%lld\n", ans);    }    return 0;}

「NOI2016」循环之美

好神啊….感觉在考场上最多推出84…

首先打个表发现所求其实就是:

injm[(j,k)=1][(i,j)=1]()

然后交换求和顺序并莫比乌斯大力算,得出:

()=dμ(d)[(d,k)=1]ndimd[(i,k)=1]

f(x)=ix[(i,k)=1],考虑这样一个序列:

0,1,2,,k,k+1,k+2,,2k,

由辗转相除法可以得出(i+pk,k)=(i,k),因此k+1..2k这段和1..k这段对答案的贡献是一样的。所以:

f(x)=xkf(k)+f(x mod k)

这样就可以大力O(nlgk)拿到84了.

剩下的比较有构造性…设g(n,k)=inμ(i)[(i,k)=1]。如果能快速求出这个就可以喜闻乐见下底函数分块了。

考虑k最小的因子p,令t为最大的k=ptq,则考虑[(i,k)=1]=[(i,q)=1][(i,q)=1][p|i]。则:

g(n,k)=inμ(i)[(i,q)=1]inμ(i)[p|i][(i,q)=1]=g(n,q)wnpμ(wp)[(wp,q)=1]

μ(ab)=[(a,b)=1]μ(a)μ(b)可知μ(wp)=[(w,p)=1]μ(w)。而[(wp,q)=1]=[(w,q)=1],因此:

g(n,k)=g(n,q)+wnpμ(p)[(w,p)=1][(w,q)=1]=g(n,q)+wnpμ(p)[(w,pq)=1]=g(n,q)+g(np,pq)

大功告成…

#include <bits/stdc++.h>using namespace std;const int MAXK = 2005, MAXN = 1000005;const int MOD = 1000007, bas = 2333;int cnt = 0;struct hash_table {    long long tab[MOD];    int n[MOD], k[MOD];    inline int hash_val(int n, int k)    { return ((long long)n*bas%MOD+k)%MOD; }    inline long long val(int __n, int __k)    {        int val = hash_val(__n, __k);        while (n[val] != __n || k[val] != __k) (val += 1) %= MOD;        return tab[val];    }    inline void insert(int __n, int __k, long long dat)    {        int val = hash_val(__n, __k);        while (k[val] != 0 || n[val] != 0) val++;        n[val] = __n, k[val] = __k, tab[val] = dat;    }    inline bool count(int __n, int __k)    {        int val = hash_val(__n, __k);        while (n[val] != __n || k[val] != __k) {            if (n[val] == 0 && k[val] == 0) return 0;            val++;        }        return 1;    }};inline int gcd(int a, int b){ return b == 0 ? a : gcd(b, a%b); }int prime[MAXN], not_prime[MAXN], mu[MAXN], top = 0;int M[MAXN];int n, m, k;int p[MAXK], q[MAXK]; // 拿出的第一个素因子; 剩余int f[MAXK];void get_prime(int n){    mu[1] = 1, not_prime[1] = 1;    for (int i = 2; i <= n; i++) {        if (!not_prime[i]) prime[++top] = i, mu[i] = -1;        for (int j = 1; j <= top && i*prime[j] <= n; j++) {            not_prime[i*prime[j]] = 1;            if (i%prime[j] == 0) {                mu[i*prime[j]] = 0;                break;            }            mu[i*prime[j]] = -mu[i];        }    }    for (int i = 1; i <= n; i++)        M[i] = M[i-1]+mu[i];}hash_table hash_M;long long get_M(int N){    if (N < MAXN) return M[N];    if (hash_M.count(N, 0)) return hash_M.val(N, 0);    long long ans = 1;    int last;    for (int i = 2; i <= N; i = last+1) {        last = N/(N/i);        ans -= (last-i+1)*get_M(N/i);    }    hash_M.insert(N, 0, ans);    return ans;}hash_table hash_s;long long get_G(int n, int k){    // cerr << n << " " << k << endl;    if (n == 0) return 0;    if (k == 1) return get_M(n);    if (hash_s.count(n, k)) return cnt++, hash_s.val(n, k);    long long tmp = get_G(n, q[k])+get_G(n/p[k], p[k]*q[k]);    hash_s.insert(n, k, tmp);    return tmp;}void init_pq(){    for (int i = 2; i <= k; i++) {        if (!not_prime[i]) p[i] = i, q[i] = 1;        else {            int fac, pp = 1;            for (int j = 1; j <= top; j++)                if (i%prime[j] == 0) {                    fac = prime[j];                    break;                }            while (i%(pp*fac) == 0) pp *= fac;            p[i] = fac, q[i] = i/pp;        }    }}void init_f(){    f[0] = 0;    for (int i = 1; i <= k; i++)         f[i] = f[i-1]+(gcd(i, k) == 1);}inline int get_F(int n){ return n/k*f[k]+f[n%k]; }int main(){    cin >> n >> m >> k;    get_prime(MAXN-1);    init_pq();    init_f();    int last;    long long ans = 0;    for (register int i = 1; i <= n && i <= m; i = last+1) {        last = min(min(n/(n/i), m/(m/i)), n);        ans += (get_G(last, k)-get_G(i-1, k))*(n/i)*get_F(m/i);    }    cout << ans << endl;    return 0;}

「NOI2016」区间

可能是NOI2016唯一一道水题吧……

#include <bits/stdc++.h>using namespace std;const int MAXN = 500005;struct segm {    int l, r, len;    friend bool operator < (const segm &a, const segm &b)    { return a.len < b.len; }} seg[MAXN];int n, m;int dx[MAXN*2], dx_top = 0;void dx_init(){    for (int i = 1; i <= n; i++)        dx[++dx_top] = seg[i].l, dx[++dx_top] = seg[i].r;    sort(dx+1, dx+dx_top+1);    dx_top = unique(dx+1, dx+dx_top+1)-dx-1;    for (int i = 1; i <= n; i++) {        seg[i].l = lower_bound(dx+1, dx+dx_top+1, seg[i].l)-dx;        seg[i].r = lower_bound(dx+1, dx+dx_top+1, seg[i].r)-dx;    }}int max_val[MAXN*4], lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4], tag[MAXN*4];int root = 0, top = 0;inline void pdw(int nd){    max_val[nd] += tag[nd];    if (lc[nd]) tag[lc[nd]] += tag[nd], tag[rc[nd]] += tag[nd];    tag[nd] = 0;}inline void update(int nd){ max_val[nd] = max(max_val[lc[nd]], max_val[rc[nd]]); }void build(int &nd, int opl, int opr){    nd = ++top, l[nd] = opl, r[nd] = opr;    if (opl < opr)        build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);}void modify(int nd, int opl, int opr, int dt){    pdw(nd);    if (l[nd] == opl && r[nd] == opr) tag[nd] += dt;    else {        int mid = (l[nd] + r[nd]) >> 1;        if (opr <= mid) modify(lc[nd], opl, opr, dt);        else if (opl > mid) modify(rc[nd], opl, opr, dt);        else modify(lc[nd], opl, mid, dt), modify(rc[nd], mid+1, opr, dt);        pdw(lc[nd]), pdw(rc[nd]), update(nd);    }}int query(int nd, int opl, int opr){    pdw(nd);    if (l[nd] == opl && r[nd] == opr) return max_val[nd];    else {        int mid = (l[nd] + r[nd]) >> 1;        if (opr <= mid) return query(lc[nd], opl, opr);        else if (opl > mid) return query(rc[nd], opl, opr);        else return max(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr));    }}inline int read(){    int a = 0, c;    do c = getchar(); while (!isdigit(c));    while (isdigit(c)) {        a = a*10+c-'0';        c = getchar();    }    return a;}int main(){    n = read(), m = read();    for (int i = 1; i <= n; i++) {        seg[i].l = read();        seg[i].r = read();        seg[i].len = seg[i].r-seg[i].l;    }    sort(seg+1, seg+n+1);    dx_init();    build(root, 1, dx_top);    int ans = INT_MAX;    int l = 1, r = 0;    while (r < n) {        while (r < n && query(root, 1, dx_top) < m)             ++r, modify(root, seg[r].l, seg[r].r, 1);        if (query(root, 1, dx_top) >= m)            ans = min(ans, seg[r].len-seg[l].len);        while (l <= n && query(root, 1, dx_top) >= m) {            if (query(root, 1, dx_top) >= m)                ans = min(ans, seg[r].len-seg[l].len);            modify(root, seg[l].l, seg[l].r, -1);            l++;        }        if (query(root, 1, dx_top) >= m)            ans = min(ans, seg[r].len-seg[l].len);    }    if (ans == INT_MAX)        puts("-1");    else printf("%d\n", ans);    return 0;}

NOI2015 寿司餐厅

每个数大于n的质因子最多只有一个,然后把相同的化为一类,大力分类讨论dp即可。

#include <bits/stdc++.h>using namespace std;const int MAXN = 505;int n;long long P;int prime[] = {2, 3, 5, 7, 11, 13, 17, 19};long long dp[2][1<<8|1][1<<8|1];long long p[2][2][1<<8|1][1<<8|1];vector<int> kind[MAXN];int main(){    scanf("%d%lld", &n, &P);    for (int i = 2; i <= n; i++) {        int S = 0, num = i;        for (int j = 0; j < 8; j++)            if (num%prime[j] == 0) {                S |= 1<<j;                while (num%prime[j] == 0) num /= prime[j];            }        if (num == 1) kind[n+1].push_back(S);        else kind[num].push_back(S);    }    int now = 0, nxt = 1;    dp[now][0][0] = 1;    for (register int i = 0; i <= n; i++) {        int nw = 0, nx = 1;        if (kind[i].empty()) continue;        memcpy(p[0][nw], dp[now], sizeof dp[now]);        memset(p[0][nx], 0, sizeof p[0][nx]);        memcpy(p[1][nw], dp[now], sizeof dp[now]);        memset(p[1][nx], 0, sizeof p[1][nx]);        for (register vector<int>::iterator l = kind[i].begin(); l != kind[i].end(); ++l) {            //cerr << i << " " << *l << endl;                for (register int j = 0; j < 1<<8; j++)                for (register int k = 0; k < 1<<8; k++) {                    (p[0][nx][j][k] += p[0][nw][j][k]) %= P;                    (p[1][nx][j][k] += p[1][nw][j][k]) %= P;                        (p[0][nx][j|(*l)][k] += p[0][nw][j][k]) %= P;                    (p[1][nx][j][k|(*l)] += p[1][nw][j][k]) %= P;                }            memset(p[0][nw], 0, sizeof p[0][nw]);            memset(p[1][nw], 0, sizeof p[1][nw]);            swap(nw, nx);        }        for (register int j = 0; j < 1<<8; j++)            for (register int k = 0; k < 1<<8; k++)                dp[now][j][k] = (p[0][nw][j][k]+p[1][nw][j][k]-dp[now][j][k])%P;    }    for (register int i = 1; i <= kind[n+1].size(); i++) {        int S = kind[n+1][i-1];        for (register int j = 0; j < 1<<8; j++)            for (register int k = 0; k < 1<<8; k++) {                long long val = dp[now][j][k];                (dp[nxt][j][k] += val) %= P;                    (dp[nxt][j][k|S] += val) %= P;                    (dp[nxt][j|S][k] += val) %= P;            }        memset(dp[now], 0, sizeof dp[now]);        swap(now, nxt);    }    long long ans = 0;    for (register int j = 0; j < 1<<8; j++) {        for (register int k = 0; k < 1<<8; k++) {            if ((j&k) == 0)                (ans += dp[now][j][k]) %= P;            //cerr << dp[now][j][k] << " ";        }        //cerr << endl;    }    printf("%lld\n", (ans+P)%P);    return 0;}

「NOI2015」荷马史诗

真…哈夫曼编码裸题

#include <bits/stdc++.h>using namespace std;const int MAXN = 100050;long long w[MAXN];int n, k;long long tot_len = 0;struct pt {    long long val;    int lev;    friend bool operator < (const pt &a, const pt &b)    { return a.val == b.val ? a.lev > b.lev : a.val > b.val; }};priority_queue<pt> heap; int main(){    scanf("%d%d", &n, &k);    for (int i = 1; i <= n; i++) scanf("%lld", &w[i]);    while ((n-1)%(k-1) != 0) w[++n] = 0;    for (int i = 1; i <= n; i++) heap.push((pt){w[i], 0});    while (heap.size() > 1) {        long long val = 0;        int lev = 0;        for (int i = 1; i <= k; i++) {            pt a = heap.top(); heap.pop();            val += a.val, lev = max(lev, a.lev);        }        tot_len += val;        heap.push((pt){val, lev+1});    }    cout << tot_len << endl << heap.top().lev << endl;    return 0;}

4545: DQS的trie

LCT维护广义后缀自动机Right集合大小…………………..
太TM难写了啊…
「不过貌似用splay维护dfs序要好写一点…什么时候尝试一个」

#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;struct  link_cut_trees {    int chl[MAXN][2], fa[MAXN], rev[MAXN];    int tag[MAXN], dat[MAXN];    int stk[MAXN], top;    bool is_rt(int x)    { return chl[fa[x]][0] != x && chl[fa[x]][1] != x;}    inline void pdw(int nd)    {        if (chl[nd][0]) tag[chl[nd][0]] += tag[nd], rev[chl[nd][0]] ^= rev[nd];        if (chl[nd][1]) tag[chl[nd][1]] += tag[nd], rev[chl[nd][1]] ^= rev[nd];        if (rev[nd]) swap(chl[nd][0], chl[nd][1]);        dat[nd] += tag[nd];        rev[nd] = 0, tag[nd] = 0;    }    void zig(int nd)    {        int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;        int son = chl[nd][tp^1];        if (son) fa[son] = p; if (!is_rt(p)) chl[g][tg] = nd;        fa[nd] = g, fa[p] = nd, chl[nd][tp^1] = p, chl[p][tp] = son;    }    void splay(int nd)    {        stk[top = 1] = nd;            for (int i = nd; !is_rt(i); i = fa[i]) stk[++top] = fa[i];        while (top) pdw(stk[top--]);        while (!is_rt(nd)) {            int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;            if (is_rt(p)) { zig(nd); break; }            if (tp == tg) zig(p), zig(nd);            else zig(nd), zig(nd);        }    }    void access(int x)    {        for (int y = 0; x; x = fa[y = x])            splay(x), chl[x][1] = y;    }    void make_rt(int x)    { access(x), splay(x), rev[x] ^= 1; }    void link(int x, int y) // x --> y    {        fa[x] = y, access(y), splay(y), tag[y] += dat[x];    }    void cut(int x)    {        access(x), splay(x), tag[chl[x][0]] -= dat[x];        fa[chl[x][0]] = 0, chl[x][0] = 0;    }    int get_dat(int x)    {        access(x), splay(x);        return dat[x];    }} ;struct SAM {    int chl[MAXN][3], fa[MAXN], maxl[MAXN], top, root;    link_cut_trees LCT;    long long ans;    SAM()    { top = root = 1, ans = 0; }    int push(int nd, int x)    {        int p = nd, np = ++top; LCT.dat[np] = 1, maxl[np] = maxl[p]+1;        while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];        if (!p) fa[np] = root, ans += maxl[np]-maxl[fa[np]], LCT.link(np, root);        else {            int q = chl[p][x];            if (maxl[q] == maxl[p]+1) fa[np] = q, ans += maxl[np]-maxl[fa[np]], LCT.link(np, q);            else {                int nq = ++top; maxl[nq] = maxl[p]+1;                memcpy(chl[nq], chl[q], sizeof chl[q]);                fa[nq] = fa[q], LCT.link(nq, fa[nq]);                ans += maxl[nq]-maxl[fa[nq]];                ans -= maxl[q]-maxl[fa[q]];                fa[q] = fa[np] = nq;                ans += maxl[q]-maxl[fa[q]], ans += maxl[np]-maxl[fa[np]];                LCT.cut(q), LCT.cut(np), LCT.link(np, nq), LCT.link(q, nq);                while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];            }        }        return np;    }    int get_pt(int nd, const char *str)    {        if (*str == '\0') return nd;        if (!chl[nd][*str-'a']) return -1;        return get_pt(chl[nd][*str-'a'], str+1);    }    inline long long diff_substring()    { return ans; }    inline int match_times(const char *str)    {        int nd = get_pt(root, str);        if (nd == -1) return 0;        return LCT.get_dat(nd);    }};struct trie {    struct node {        int to, next;        char dis;    } edge[MAXN*2];    int head[MAXN], top;    inline void push(int i, int j, char c)    { edge[++top] = (node){j, head[i], c}, head[i] = top; }    int stat[MAXN], vis[MAXN];    int root;    SAM autometa;    queue<int> que;    trie()    { root = 1, stat[root] = 1, top = 0; }        void dfs(int x)    {        vis[x] = 1;        for (int i = head[x]; i; i = edge[i].next) {            int to = edge[i].to, c = edge[i].dis-'a';            if (vis[to]) continue;            stat[to] = autometa.push(stat[x], c);                dfs(to);        }    }    inline int match_times(const char *str)    { return autometa.match_times(str); }    inline long long diff_substring()    { return autometa.diff_substring(); }} T;int n, m;int id;char str[MAXN];int main(){    scanf("%d%d", &id, &n);    for (int i = 1; i < n; i++) {        int u, v; char c;        scanf("%d%d %c\n", &u, &v, &c);        T.push(u, v, c), T.push(v, u, c);    }    T.dfs(1);    scanf("%d", &m);    for (int i = 1; i <= m; i++) {        int opt, r, t;        scanf("%d", &opt);        if (opt == 1) {            printf("%lld\n", T.diff_substring());        } else if (opt == 2) {            scanf("%d%d", &r, &t), t--;            while (t--) {                int u, v;                char c;                scanf("%d%d %c\n", &u, &v, &c);                T.push(u, v, c), T.push(v, u, c);            }            T.dfs(r);        } else if (opt == 3) {            scanf("%s", str);            printf("%d\n", T.match_times(str));        } else throw;    }    return 0;}