bzoj刷题记录4.10-4.14

来源:互联网 发布:淘宝网上批发市场 编辑:程序博客网 时间:2024/06/05 00:32

bzoj刷题记录4.10-4.14


bzoj1093: [ZJOI2007]最大半联通子图

仍然缩点。缩完点之后每个半联通子图就相当于一条带权链(权值是一个scc的点数)。之后dp即可。

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005, MAXM = 2000005;struct g {    struct node {        int to, next;    } edge[MAXM];    int head[MAXN], top;    g(){ top = 0, memset(head, 0, sizeof head); }    void push(int i, int j)    { edge[++top] = (node) {j, head[i]}, head[i] = top; }}g, g1;int dfn[MAXN], low[MAXN], stk[MAXN], top = 0, dft = 0;int gp[MAXN], gtop = 0, gsiz[MAXN], vis[MAXN], n, m;void tarjan(int nd){    dfn[nd] = low[nd] = ++dft;    stk[++top] = nd, vis[nd] = 1;    for (int i = g.head[nd]; i; i = g.edge[i].next) {        int to = g.edge[i].to;        if (!dfn[to]) tarjan(to), low[nd] = min(low[nd], low[to]);        else if (vis[to]) low[nd] = min(low[nd], dfn[to]);    }    if (dfn[nd] == low[nd]) {        int now; ++gtop;        do {            now = stk[top--], vis[now] = 0, gp[now] = gtop, gsiz[gtop]++;        } while (now != nd);    }}long long x;set<pair<int,int> > hst;int dp[MAXN], cnt[MAXN];int dfs(int nd){    if (dp[nd] != -1) return dp[nd];    dp[nd] = gsiz[nd];    for (int i = g1.head[nd]; i; i = g1.edge[i].next)        dp[nd] = max(dp[nd], dfs(g1.edge[i].to)+gsiz[nd]);    return dp[nd];}int dfs_cnt(int nd){    if (cnt[nd] != -1) return cnt[nd];    if (!g1.head[nd]) return 1%x;    cnt[nd] = 0;    for (int i = g1.head[nd]; i; i = g1.edge[i].next) {        int to = g1.edge[i].to;        if (dp[nd] == dp[to] + gsiz[nd]) (cnt[nd] += dfs_cnt(to)) %= x;    }    return cnt[nd];}void work(){    for (int i = 1; i <= n; i++)        if (!dfn[i])            tarjan(i);    for (int i = 1; i <= n; i++)        for (int j = g.head[i]; j; j = g.edge[j].next) {            int to = g.edge[j].to;            if (gp[i] != gp[to] && !hst.count(make_pair(gp[i], gp[to]))) {                hst.insert(make_pair(gp[i], gp[to]));                g1.push(gp[to], gp[i]); // G^R            }        }    memset(dp, -1, sizeof dp), memset(cnt, -1, sizeof cnt);    int ans1; long long ans2; ans1 = ans2 = 0;    for (int i = 1; i <= gtop; i++)        ans1 = max(ans1, dfs(i));    for (int i = 1; i <= gtop; i++)        if (dfs(i) == ans1)            (ans2 += dfs_cnt(i)) %= x;    printf("%d\n%lld\n", ans1, ans2);}int main(){    scanf("%d%d%lld", &n, &m, &x);    for (int i = 1; i <= m; i++) {        int u, v; scanf("%d%d", &u, &v), g.push(u, v);    }    work();    return 0;}

bzoj2208: [JSOI2010] 联通数

缩点后暴力可过..
但是貌似有更好的算法??比如在拓扑图上随意dfs一棵生成树然后dfs序维护??
尚不清楚复杂度是否更优。

#include <bits/stdc++.h>using namespace std;const int MAXN = 2005, MAXM = MAXN*MAXN;struct graph {    struct node {        int to, next;    } edge[MAXM];    int head[MAXN], top;    graph(){top = 0, memset(head, 0, sizeof head); }    void push(int i, int j)    { ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }} g, g1;int dfn[MAXN], dft = 0, low[MAXN], vis[MAXN];int stk[MAXN], top = 0;int gp[MAXN], gtop = 0, gsiz[MAXN], n;void tarjan(int nd){    vis[nd] = 1, dfn[nd] = low[nd] = ++dft, stk[++top] = nd;    for (int i = g.head[nd]; i; i = g.edge[i].next) {        int to = g.edge[i].to;        if (!dfn[to]) tarjan(to), low[nd] = min(low[nd], low[to]);        else if (vis[to]) low[nd] = min(low[nd], dfn[to]);    }    if (dfn[nd] == low[nd]) {        int now; ++gtop; //printf(" -- Group %d --\n", gtop);        do {            now = stk[top--];            gp[now] = gtop, gsiz[gtop]++, vis[now] = 0;            // cerr << now << " ";        } while(now != nd); //puts("");    }}int bfstime = 0;queue<int> que;int lkt[MAXN][MAXN];void work(){    for (int i = 1; i <= n; i++)        if (!dfn[i])            tarjan(i);    for (int i = 1; i <= n; i++)        for (int j = g.head[i]; j; j = g.edge[j].next) {            int to = g.edge[j].to;            if (gp[to] != gp[i] && !lkt[gp[i]][gp[to]]) {                lkt[gp[i]][gp[to]] = 1;                g1.push(gp[i], gp[to]);            }        }    int ans = 0;    memset(vis, 0, sizeof vis);    for (int i = 1; i <= gtop; i++) {        ++bfstime;        que.push(i), vis[i] = bfstime;        int cnt = 0;        while (!que.empty()) {            int t = que.front(); que.pop(), cnt += gsiz[t];            for (int i = g1.head[t]; i; i = g1.edge[i].next) {                int to = g1.edge[i].to;                if (vis[to] != bfstime)                    vis[to] = bfstime, que.push(to);            }        }        ans += cnt*gsiz[i];    }    cout << ans << endl;}char str[2005];int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++) {        scanf("%s", str+1);        for (int j = 1; j <= n; j++)            if (str[j] == '1')                g.push(i, j);    }    work();    return 0;}

4.11 更新

今天来一波字符串。

bzoj3172: [Tjoi2013]单词

这个题就比较神了。首先建出AC自动机,然后先求出每个节点的“顺着路过的总数dp[nd]”,也就是trie树上子树finish标记的总数;然后每个节点表示的串出现的次数就是某个节点fail树的子树中所有dp[nd]的总和。

第一次做反了…
形如

shershersher

的数据可以hack。因为shers不会算入her中去。

#include <bits/stdc++.h>using namespace std;const int MAXN = 3000005;int chl[MAXN][26], fail[MAXN], fin[MAXN], top = 0, root = 0;vector<int> ids[MAXN];int new_node(){ return ++top, fail[top] = fin[top] = 0, memset(chl[top],0, sizeof chl[top]), top; }void push(int &nd, const char *str, int id){    if (nd == 0) nd = new_node();    if (*str == '\0') fin[nd]++, ids[nd].push_back(id);    else push(chl[nd][*str-'a'], str+1, id);}struct node {    int to, next;} edge[MAXN];int head[MAXN], tp_edge = 0;void push(int i, int j){ ++tp_edge, edge[tp_edge] = (node) {j, head[i]}, head[i] = tp_edge; }int siz[MAXN], n;queue<int> que;void init(){    que.push(root); fail[root] = 0;    while (!que.empty()) {        int t = que.front(); que.pop();        for (int i = 0; i < 26; i++) if (chl[t][i]) {            int p = fail[t];            while (p && !chl[p][i]) p = fail[p];            if (!p) fail[chl[t][i]] = root;            else fail[chl[t][i]] = chl[p][i];            que.push(chl[t][i]);            push(fail[chl[t][i]], chl[t][i]);        }    }}long long res[MAXN];long long dp[MAXN];int dfs(int nd){    if (siz[nd] != -1) return siz[nd];    siz[nd] = dp[nd];    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        siz[nd] += dfs(to);    }    return siz[nd];}long long dfs2(int nd){    if (dp[nd] != -1) return dp[nd];    dp[nd] = fin[nd];    for (int i = 0; i < 26; i++)        if (chl[nd][i])            dp[nd] += dfs2(chl[nd][i]);    return dp[nd];}char str[MAXN];int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++) {        scanf("%s", str);        push(root, str, i);    }    init();    memset(siz, -1, sizeof siz);    memset(dp, -1, sizeof dp);    dfs2(root);    dfs(root);    for (int i = 1; i <= top; i++)        for (int j = 0; j < fin[i]; j++)            res[ids[i][j]] = siz[i];    for (int i = 1; i <= n; i++)        printf("%lld\n", res[i]);    return 0;}

bzoj1090: [SCOI2003]字符串折叠

首先|S|100告诉我们必然不是什么高端结构…

区间dp是正解,方程容易想到,唯一的字符串trick就是用hashk判断相等。

特别注意取某一区间hash的小技巧。

#include <bits/stdc++.h>using namespace std;const int MAXN = 105;char str[MAXN];int n;unsigned long long hash_tab[MAXN], pwr[MAXN];unsigned long long hash_val(int i, int j){ return hash_tab[j]-hash_tab[i-1]*pwr[j-i+1]; }int dp[MAXN][MAXN];int len(int num){ return num <= 9 ? 1 : (num <= 99 ? 2 : 3); }int dfs(int i, int j){    if (dp[i][j] != -1) return dp[i][j];    if (i == j) return 1;    dp[i][j] = INT_MAX;    for (int k = i; k < j; k++)        dp[i][j] = min(dp[i][j], dfs(i,k)+dfs(k+1,j));    for (int k = 1; k <= j-i+1; k++) { // 枚举每一块长度        if ((j-i+1)%k != 0) continue;        int flag = 1; unsigned long long val = 0;        for (int p = 0; p < (j-i+1)/k; p++) {            if (p == 0) val = hash_val(i+p*k, i+(p+1)*k-1);            else if (val != hash_val(i+p*k, i+(p+1)*k-1)) { flag = 0; break; }        }        if (flag) dp[i][j] = min(dp[i][j], dfs(i, i+k-1)+2+len((j-i+1)/k));    }    return dp[i][j];}int main(){    scanf("%s", str+1), n = strlen(str+1);    pwr[0] = 1;    for (int i = 1; i <= n; i++) pwr[i] = pwr[i-1]*31;    for (int i = 1; i <= n; i++) hash_tab[i] = hash_tab[i-1]*31+(str[i]-'A'+1);    memset(dp, -1, sizeof dp);    cout << dfs(1, n) << endl;    return 0;}

bzoj2124: 等差子序列

乍一看和字符串毫无关系…
事实上,存在一个等差数列当且仅当存在三个点i<j<k,满足:

ai=aj+d,ak=ajd

然后对于这种对称的处理思路就两种:

  1. FFT
  2. 回文串

FFT貌似不太好处理,但回文串很兹瓷啊。用线段树记录每个点是否存在1/2并维护区间哈希,然后正反算哈希判断是不是相等即可。如果在某一时刻iai两侧不对称,就必然有一段回文串,也就有等差数列。

#include <bits/stdc++.h>using namespace std;const int MAXN = 10005;unsigned long long hash_tab[MAXN*4], hash_rev[MAXN*4], power[MAXN];int lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4], top = 0, root = 0;int n, a[MAXN];inline int siz(int nd) { return r[nd]-l[nd]+1; }void build(int &nd, int opl, int opr){    nd = ++top, hash_tab[nd] = hash_rev[nd] = 1, lc[nd] = rc[nd] = 0, l[nd] = opl, r[nd] = opr;    if (opl < opr) {        build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);        hash_tab[nd] = hash_tab[lc[nd]]+power[siz(lc[nd])]*hash_tab[rc[nd]];        hash_rev[nd] = hash_rev[rc[nd]]+power[siz(rc[nd])]*hash_rev[lc[nd]];    }}void modify(int nd, int pos){    if (l[nd] == r[nd]) hash_tab[nd] = hash_rev[nd] = 2;    else {        modify(pos<=(l[nd]+r[nd])/2?lc[nd]:rc[nd], pos);        hash_tab[nd] = hash_tab[lc[nd]]+power[siz(lc[nd])]*hash_tab[rc[nd]];        hash_rev[nd] = hash_rev[rc[nd]]+power[siz(rc[nd])]*hash_rev[lc[nd]];    }}unsigned long long query(int nd, int opl, int opr){    if (l[nd] == opl && r[nd] == opr) return hash_tab[nd];    else {        int mid = (l[nd]+r[nd])/2;        if (opr <= mid) return query(lc[nd], opl, opr);        else if (opl > mid) return query(rc[nd], opl, opr);        else return query(lc[nd], opl, mid)+power[mid-opl+1]*query(rc[nd], mid+1, opr);    }}unsigned long long query_reversed(int nd, int opl, int opr){    if (l[nd] == opl && r[nd] == opr) return hash_rev[nd];    else {        int mid = (l[nd]+r[nd])/2;        if (opr <= mid) return query_reversed(lc[nd], opl, opr);        else if (opl > mid) return query_reversed(rc[nd], opl, opr);        else return power[opr-mid]*query_reversed(lc[nd], opl, mid)+query_reversed(rc[nd], mid+1, opr);    }}void init(){    scanf("%d", &n);    top = root = 0;    build(root, 1, n);    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);}int main(){    int T; scanf("%d", &T);    power[0] = 1;    for (int i = 1; i <= 10000; i++) power[i] = power[i-1]*31;    while (T--) {        init();        int flag = 0;        for (int i = 1; i <= n; i++) {            // cerr << query(root, 1, 3) << "--" << query_reversed(root, 1, 3) << endl;            if (a[i] <= n-a[i]+1) {                if (i >= 2 && i < n && query(root, 1, 2*a[i]-1) != query_reversed(root, 1, 2*a[i]-1)) {                    flag = 1; break;                }            } else {                int k = 2*(n-a[i]+1)-1;                if (i >= 2 && i < n && query(root, n-k+1, n) != query_reversed(root, n-k+1, n)) {                    flag = 1; break;                }            }            modify(root, a[i]);        }        if (flag) puts("Y");        else puts("N");    }    return 0;}

bzoj3207: 花神的嘲讽计划Ⅰ

首先k20就可以暴力算出来每个块的哈希,然后就变成一个在区间内有没有某个值的问题了。莫队可解,O(nn)但跑比谁都快。

注意:hash一定要用ull…

#include <bits/stdc++.h>using namespace std;const int MAXN = 400005, lgn = 20;int n, m, k, bas, sqr;unsigned long long hash_val(int a[], int l, int r){    unsigned long long ans = 0;    for (int i = l; i <= r; i++)        ans = ans*bas+a[i];    return ans;}struct query {    int l, r, dat, id, ans;    friend bool operator < (const query &a, const query &b)    { return a.l/sqr != b.l/sqr ? a.l < b.l : a.r < b.r; }} q[MAXN];bool cmp(const query &a, const query &b){ return a.id < b.id; }int a[MAXN], tmp[30];unsigned long long d[MAXN], dx[MAXN], qu[MAXN], top = 0;void dx_init(){    memcpy(dx, d, sizeof d);    sort(dx+1, dx+top+1);}int dx_num(unsigned long long dat){ return lower_bound(dx+1, dx+top+1, dat)-dx; }int tab[MAXN];int main(){    scanf("%d%d%d", &n, &m, &k), bas = n+1;    sqr = sqrt(n); // 分块大小    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);    for (int i = 1; i+k-1 <= n; i++) d[++top] = hash_val(a, i, i+k-1);    for (int i = 1; i <= m; i++) {        scanf("%d%d", &q[i].l, &q[i].r); q[i].r = q[i].r-k+1;        for (int j = 1; j <= k; j++) scanf("%d", &tmp[j]);        qu[i] = hash_val(tmp, 1, k);        d[++top] = qu[i], q[i].id = i;    }    dx_init(); // 离散化    for (int i = 1; i+k-1 <= n; i++) a[i] = dx_num(d[i]);    for (int i = 1; i <= m; i++) q[i].dat = dx_num(qu[i]);    sort(q+1, q+m+1);    memset(tab, 0, sizeof tab);    int L = q[1].l, R = q[1].r;    for (int i = L; i <= R; i++) tab[a[i]]++; q[1].ans = tab[q[1].dat];    for (int i = 2; i <= m; i++) {        if (q[i].l > q[i].r) continue;        while (L < q[i].l) tab[a[L++]]--;        while (L > q[i].l) tab[a[--L]]++;        while (R < q[i].r) tab[a[++R]]++;        while (R > q[i].r) tab[a[R--]]--;        q[i].ans = tab[q[i].dat];    }    sort(q+1, q+m+1, cmp);    for (int i = 1; i <= m; i++)        if (q[i].ans) puts("No");        else puts("Yes");    return 0;}

bzoj1103: [POI2007]大都市meg

看题解dfs序+树状数组就水过去了,O(nlgn)
但是写了链剖O(nlg2n)也水过去了。
然而O(nlgn)的lct却死超死超的…
玄学啊…当然也可能我写跪了…

链剖

#include <bits/stdc++.h>using namespace std;const int MAXN = 250005;int C[MAXN], n, dstop = 0;int lowbit(int i) { return i&(-i); }void modify(int pos, int dt){ for (int i = pos; i <= n; i += lowbit(i)) C[i] += dt; }int sum(int pos){    int ans = 0;    for (int i = pos; i > 0; i -= lowbit(i)) { ans += C[i];}    return ans;}int sum(int l, int r){ return sum(r)-sum(l-1); }struct node {    int to, next;} edge[MAXN*2];int head[MAXN], top;void push(int i, int j){ ++top, edge[top] = (node){j, head[i]}, head[i] = top; }int siz[MAXN], id[MAXN], ind[MAXN], fa[MAXN];void dfs1(int nd){    siz[nd] = 1;    for (int i = head[nd]; i; i = edge[i].next)        dfs1(edge[i].to), siz[nd] += siz[edge[i].to], fa[edge[i].to] = nd;}void dfs2(int nd, int from = 0){    id[nd] = ++dstop, ind[nd] = from, modify(id[nd], 1);    int hev = 0;    for (int i = head[nd]; i; i = edge[i].next)        if (siz[edge[i].to] > siz[hev])            hev = edge[i].to;    if (!hev) return;    dfs2(hev, from);    for (int i = head[nd]; i; i = edge[i].next)        if (edge[i].to != hev)            dfs2(edge[i].to, edge[i].to);}void change(int nd){ modify(id[nd], -1); }int ask(int nd){    int ans = 0;    while (nd) {        // cout << nd << endl;        ans += sum(id[ind[nd]], id[nd]), nd = fa[ind[nd]];    }    return ans;}int main(){    scanf("%d", &n);    for (int i = 1; i < n; i++) {        int u, v; scanf("%d%d", &u, &v);        if (u > v) swap(u,v);        push(u, v);    }    dfs1(1), dfs2(1);    int m; scanf("%d", &m);    for (int i = 1; i <= m+n-1; i++) {        char s; int u, v;        scanf("\n%c", &s);        if (s == 'A') { scanf("%d%d", &u, &v), change(max(u,v)); }        else if (s == 'W') {scanf("%d", &u), printf("%d\n", ask(u)-1); }        else throw;    }    return 0;}

LCT(TLE)

#include <bits/stdc++.h>using namespace std;const int MAXN = 250005;int chl[MAXN][2], dat[MAXN], hv[MAXN], stk[MAXN], stktop = 0, fa[MAXN];int rev[MAXN], n, m;bool is_rt(int nd){ return nd != chl[fa[nd]][0] && nd != chl[fa[nd]][1]; }void update(int nd){ dat[nd] = dat[chl[nd][0]] + dat[chl[nd][1]] + hv[nd];}void pdw(int nd){    if (!rev[nd]) return;    int &lc = chl[nd][0], &rc = chl[nd][1]; rev[nd] = 0;    if (lc) rev[lc] ^= 1;    if (rc) rev[rc] ^= 1;    swap(lc, rc);}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 (!is_rt(p)) chl[g][tg] = nd; chl[nd][tp^1] = p, chl[p][tp] = son;    fa[son] = p, fa[p] = nd, fa[nd] = g;    update(p), update(nd);}void splay(int nd){    stk[stktop = 1] = nd;    for (int i = nd; !is_rt(i); i = fa[i]) stk[++stktop] = fa[i];    while (stktop) pdw(stk[stktop--]);    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; }        else 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, update(x);}void make_rt(int x){ access(x), splay(x), rev[x] ^= 1; }void link(int i, int j){ make_rt(j), fa[j] = i, access(j); }void change(int nd){ make_rt(nd), splay(nd), hv[nd]--, update(nd); }int ask(int nd){ make_rt(1), access(nd), splay(nd); return hv[nd]+dat[chl[nd][0]]; }int main(){    scanf("%d", &n);    for (int i = 2; i <= n; i++) hv[i] = 1;    for (int i = 1; i < n; i++) {        int u, v; scanf("%d%d", &u, &v);        if (u > v) swap(u, v);        link(u, v);    }    scanf("%d", &m);    for (int i = 1; i <= n+m-1; i++) {        char c; int u, v;        scanf("\n%c", &c);        if (c == 'W') { scanf("%d", &u), printf("%d\n", ask(u)); }        else if (c == 'A') { scanf("%d%d", &u, &v), change(max(u, v)); }    }    return 0;}

dfn&BIT

#include <bits/stdc++.h>using namespace std;const int MAXN = 250005;int C[MAXN], n, dstop = 0;int lowbit(int i) { return i&(-i); }void modify(int pos, int dt){ for (int i = pos; i <= n; i += lowbit(i)) C[i] += dt; }int sum(int pos){    int ans = 0;    for (int i = pos; i > 0; i -= lowbit(i)) { ans += C[i];}    return ans;}struct node {    int to, next;} edge[MAXN];int head[MAXN], top = 0;void push(int i, int j){ ++top, edge[top] = (node){j, head[i]}, head[i] = top; }int depth[MAXN], dfi[MAXN], dfo[MAXN], dft = 0;void dfs(int nd){    dfi[nd] = ++dft;    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        depth[to] = depth[nd]+1;        dfs(to);    }    dfo[nd] = dft;}void change(int nd){ modify(dfi[nd], 1), modify(dfo[nd]+1, -1); }int ask(int nd){ return depth[nd]-sum(dfi[nd]); }int main(){    scanf("%d", &n);    for (int i = 1; i < n; i++) {        int u, v; scanf("%d%d", &u, &v);        if (u > v) swap(u,v);        push(u, v);    }    depth[1] = 0, dfs(1);    int m; scanf("%d", &m);    for (int i = 1; i <= n+m-1; i++) {        char s; int u, v;        scanf("\n%c", &s);        if (s == 'A') { scanf("%d%d", &u, &v), change(max(u,v)); }        else if (s == 'W') {scanf("%d", &u), printf("%d\n", ask(u)); }        else throw;    }    return 0;}

4.13

bzoj3506: [Cqoi2014]排序机械臂

题面传送门

就是splay维护最小值和最小值位置,加上反转操作即可。
然而debug能力是硬伤…
注意重复元素的处理,就是xixi(n+1)+i

#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;int chl[MAXN][2], fa[MAXN], rev[MAXN], stk[MAXN], stktop = 0;int top = 0, min_pos[MAXN], siz[MAXN], root = 0;long long min_val[MAXN], dat[MAXN];void update(int nd){    min_val[nd] = min(dat[nd], min(min_val[chl[nd][0]], min_val[chl[nd][1]]));    min_pos[nd] = min_val[nd] == min_val[chl[nd][0]] ? min_pos[chl[nd][0]] : (min_val[nd] == dat[nd] ? nd : min_pos[chl[nd][1]]);    siz[nd] = siz[chl[nd][0]] + siz[chl[nd][1]] + 1;}inline int new_node(long long d) { return ++top, dat[top] = d, update(top), top;}void pdw(int nd){    if (!rev[nd]) return;    int &lc = chl[nd][0], &rc = chl[nd][1];    if (lc) rev[lc] ^= 1;    if (rc) rev[rc] ^= 1;    swap(lc, rc), rev[nd] ^= 1;}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];    fa[son] = son==0?0:p, fa[p] = nd, fa[nd] = g;    chl[g][tg] = g==0?0:nd, chl[nd][tp^1] = p, chl[p][tp] = son;    root = g?root:nd;    update(p), update(nd);}void splay(int nd, int tar = 0){    stk[stktop = 1] = nd;    for (int i = nd; fa[i]; i = fa[i]) stk[++stktop] = fa[i];    for (; stktop; stktop--) pdw(stk[stktop]);    while (fa[nd] != tar) {        int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;        if (g == tar) {zig(nd); break; }        else if (tp == tg) zig(p), zig(nd);        else zig(nd), zig(nd);    }}void dfs(int nd){    if (!nd) return;    pdw(nd);    dfs(chl[nd][0]);    cerr << dat[nd] << " ";    dfs(chl[nd][1]);}void insert(long long nd){    if (root == 0) root = new_node(nd);    else {        chl[root][1] = new_node(nd), fa[top] = root;        splay(top), update(top);        // dfs(top);    }}int get_min(){    splay(min_pos[root]);    int ans = siz[chl[root][0]]+1;    return ans;}int get_by_order(int k){    int nd = root; k--; // have k element on left    while (nd) {        pdw(nd), update(nd);        if (siz[chl[nd][0]] == k) break;        nd = siz[chl[nd][0]]>k?chl[nd][0]:(k -= siz[chl[nd][0]]+1, chl[nd][1]);    }    return nd;}int get_segment(int l, int r){    if (l == 1 && r == siz[root]) return root;    if (l == 1) return splay(get_by_order(r+1)), chl[root][0];    if (r == siz[root]) return splay(get_by_order(l-1)), chl[root][1];    return splay(get_by_order(l-1)), splay(get_by_order(r+1), root), chl[chl[root][1]][0];}void del_root(){    int t = get_segment(siz[chl[root][0]]+1, siz[chl[root][0]]+1);    int tp = chl[fa[t]][0] != t; chl[fa[t]][tp] = 0;    for (int i = fa[t]; i; i = fa[i]) update(i);    fa[t] = 0;}void reverse(int l, int r){    int t = get_segment(l, r);    rev[t] ^= 1;}int ans(){    int ret = get_min();    reverse(1, ret);    splay(min_pos[root]), del_root();    return ret;}int n;int main(){    memset(min_val, 127/3, sizeof min_val);    scanf("%d", &n);    for (int i = 1; i <= n; i++) {        int u; scanf("%d", &u); insert((long long)u*(n+1)+i);    }    for (int i = 1; i <= n; i++) printf("%d ", ans()+i-1);    return 0;}

bzoj3530: [Sdoi2014]数数

大爷题解:http://blog.csdn.net/aarongzk/article/details/50472209#

就是AC自动机上dp,然而数位dp仍然跪烂…要注意前导零的处理。
AC自动机忘记push儿子调试1h…

#include <bits/stdc++.h>using namespace std;const int MAXL = 1505;int vis[MAXL];struct ACM {    struct node {        int chl[10];        int finished, fail;        node() { memset(chl, 0, sizeof chl), finished = fail = 0; }    } tree[MAXL];    int top, root;    ACM() { top = root = 0; }    inline int new_node() { return ++top; }    void insert(int &nd, const char *str)    {        if (!nd) nd = new_node();        if (*str == '\0') tree[nd].finished = 1, vis[nd] = 1;        else insert(tree[nd].chl[*str-'0'], str+1);    }    queue<int> que;    void init()    {        tree[root].fail = 0, que.push(root);        while (!que.empty()) {            int nd = que.front(); que.pop(); vis[nd] |= vis[tree[nd].fail];            for (int i = 0; i < 10; i++) {                if (!tree[nd].chl[i]) continue;                int p = tree[nd].fail;                while (p && !tree[p].chl[i]) p = tree[p].fail;                if (!p) tree[tree[nd].chl[i]].fail = root;                else tree[tree[nd].chl[i]].fail = tree[p].chl[i];                que.push(tree[nd].chl[i]);            }        } ;    }    int trans(int nd, int ths)    {        if (tree[nd].chl[ths]) return tree[nd].chl[ths];        int p = tree[nd].fail;        while (p && !tree[p].chl[ths]) p = tree[p].fail;        if (!p) return root;        else return tree[p].chl[ths];    }} ac;long long dp[1500][MAXL][3]; // 1 随意 0 不随意char str[MAXL]; int s[MAXL];long long mod = 1e9+7;int n, m;int main(){    memset(vis, 0, sizeof vis);    scanf("%s", str+1); n = strlen(str+1);    for (int i = 1; i <= n; i++) s[i] = str[i]-'0';    scanf("%d", &m);    for (int i = 1; i <= m; i++) {        scanf("%s", str);        ac.insert(ac.root, str);    }    long long ans = 0;    ac.init();    memset(dp, 0, sizeof dp);    for (int i = 1; i < 10; i++) if (!vis[ac.trans(ac.root, i)]) dp[2][ac.trans(ac.root, i)][0]++;    for (int i = 2; i < n; i++)        for (int j = 1; j <= ac.top; j++)            for (int k = 0; k < 10; k++) if (!vis[ac.trans(j, k)])                (dp[i+1][ac.trans(j, k)][0] += dp[i][j][0]) %= mod;    for (int i = 2; i <= n; i++)        for (int j = 1; j <= ac.top; j++)            (ans += dp[i][j][0]) %= mod; // 前导0    memset(dp, 0, sizeof dp);    for (int i = 1; i <= s[1]; i++)        if (!vis[ac.trans(ac.root, i)])            dp[2][ac.trans(ac.root, i)][i == s[1]]++;    for (int i = 2; i <= n; i++) {        for (int j = 1; j <= ac.top; j++)            for (int k = 0; k < 10; k++) {                if (vis[ac.trans(j, k)]) continue;                (dp[i+1][ac.trans(j, k)][0] += dp[i][j][0]) %= mod;                if (k < s[i]) (dp[i+1][ac.trans(j, k)][0] += dp[i][j][1]) %= mod;                else if (k == s[i]) (dp[i+1][ac.trans(j, k)][1] += dp[i][j][1]) %= mod;            }    }    for (int i = 1; i <= ac.top; i++)        (ans += dp[n+1][i][0]+dp[n+1][i][1]) %= mod;    cout << ans << endl;    return 0;}
0 0