2017 ACM/ICPC Asia Regional Shenyang Online


6194 string string string 后缀自动机 - 求恰好出现k次的字符串

hdu 6194 string string string

#include <bits/stdc++.h>
int k;
using namespace std;
typedef long long ll;
ll res1, res2;
namespace Sam { // 后缀自动机 板子

    const int M = 1e5 + 1;
    const int CHAR_NUM = 26;

    // 后缀自动机
    struct SAM {
        struct state {
            int len, link, cnt;
            vector<int> nxt;

            state() { nxt.resize(CHAR_NUM); }
        } st[M * 2];

        int sz;//自动机的大小
        int last;// 未加入下一个字符前最长的前缀(整个串)所属的节点的编号

        void clear(vector<int> &a) {
            for (int i = 0; i < CHAR_NUM; i++) {
                a[i] = 0;
            }
        }

        void init() {
            // 创建只有一个初始状态的sam
            st[0].len = 0;
            st[0].link = -1;
            st[0].cnt = 0;
            clear(st[0].nxt);
            sz = 1;
            last = 0;
        }

        ll insert(int c, int k) {
            int cur = sz++;
            st[cur].cnt = 0;
            st[cur].len = st[last].len + 1;
            clear(st[cur].nxt);
            st[cur].link = -1;
            int p = last;
            while (p != -1 && !st[p].nxt[c]) {
                st[p].nxt[c] = cur;
                p = st[p].link;
            }

            if (p == -1) {
                st[cur].link = 0;
            } else {
                int q = st[p].nxt[c];
                if (st[p].len + 1 == st[q].len) {
                    st[cur].link = q;

                } else {
                    int clone = sz++;//复制
                    st[clone].len = st[p].len + 1;
                    st[clone].nxt = st[q].nxt;
                    st[clone].link = st[q].link;
                    st[clone].cnt = st[q].cnt;
                    while (p != -1 && st[p].nxt[c] == q) {
                        st[p].nxt[c] = clone;
                        p = st[p].link;
                    }
                    st[q].link = st[cur].link = clone;
                }
            }
            last = cur;

            int now = last;
            ll res = 0;
            while (now != -1) {
                if (st[now].cnt >= k + 1) {
                    break;
                }
                st[now].cnt++;
                if (st[now].cnt == k) {
                    res1 += st[now].len - st[st[now].link].len;
                }
                if (st[now].cnt == k + 1) {
                    res2 += st[now].len - st[st[now].link].len;
                }
                now = st[now].link;
            }
            return res;
        }
    } sam;
}
using namespace Sam;

string s;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T;
    cin >> T;
    for (int cs = 1; cs <= T; cs++) {
        cin >> k;
        cin >> s;
        int len = s.length();
        sam.init();
        res1 = 0, res2 = 0;
        for (int i = 0; i < len; i++) {
            res1 += sam.insert(s[i] - 'a', k);
        }
        cout << res1 - res2 << endl;
    }

    return 0;
}

6195 cable cable cable

hdu 6195 cable cable cable

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
    ll n, k;
    while (~scanf("%lld%lld", &n, &k)) {
        printf("%lld\n", k + k * (n - k));
    }
    return 0;
}

6196 happy happy happy
6196 happy happy happy

6197 array array array 最长非递减子序列

hdu 6197 array array array

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int inf = 0x3f3f3f3f;
int b[MAXN];
int a[MAXN];
int dp[MAXN];

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, k;
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++)scanf("%d", &a[i]), dp[i] = inf;
        for (int i = 1; i <= n; ++i) {
            *lower_bound(dp + 1, dp + i, a[i]) = a[i];
        }
        int len1 = lower_bound(dp + 1, dp + n + 1, inf) - dp - 1;
        int nn = n;
        for (int i = 1; i <= n; i++)b[i] = a[nn--], dp[i] = inf;
        for (int i = 1; i <= n; ++i) {
            *lower_bound(dp + 1, dp + i, b[i]) = b[i];
        }
        int len2 = lower_bound(dp + 1, dp + n + 1, inf) - dp - 1;
 
        if (len1 >= n - k || len2 >= n - k)
            printf("A is a magic array.\n");
        else 
            printf("A is not a magic array.\n");
    }
    return 0;
}

6198 number number number 找规律 + 线性递推式 + 矩阵快速幂

hdu 6198 number number number

根据前20个斐波那契数,暴力打表找到前5个数:4、12、33、88、232

再暴力打表得出任意一个满足要求的线性递推式:

F n = 3 F n − 1 − F n − 2 + 1 ( F 0 = 4 , F 1 = 12 ) F_n=3F_{n-1}-F_{n-2}+1 (F_0=4 ,F_1=12) Fn=3Fn1Fn2+1(F0=4,F1=12)

再转化成矩阵形式:
[ F n F n − 1 1 ] = [ 3 − 1 1 1 0 0 0 0 1 ] × [ F n − 1 F n − 2 1 ] \begin{bmatrix} F_n\\F_{n-1}\\1\end{bmatrix}=\begin{bmatrix}3 &-1 & 1\\1&0&0\\0&0&1\end{bmatrix} \times \begin{bmatrix} F_{n-1}\\F_{n-2}\\1\end{bmatrix} FnFn11=310100101×Fn1Fn21
直接上矩阵快速幂板子

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace Matrix {//矩阵快速幂板子
    const int maxn = 3;
    ll mod = 998244353;

    struct Mat {
        ll mat[maxn][maxn];

        void clear() {
            memset(mat, 0, sizeof(mat));
        }

        Mat operator*(const Mat a) const {
            Mat b;
            b.clear();

            for (int i = 0; i < maxn; ++i) {
                for (int j = 0; j < maxn; ++j) {
                    for (int k = 0; k < maxn; ++k) {
                        b.mat[i][j] = (b.mat[i][j] + (mat[i][k] * a.mat[k][j]) % mod + mod) % mod;
                    }
                }
            }
            return b;
        }
    };

    Mat pow(Mat m, ll k) {
        Mat res;
        res.clear();

        for (int i = 0; i < maxn; ++i) {//单位矩阵
            res.mat[i][i] = 1;
        }

        while (k) {
            if (k & 1)res = res * m;
            k >>= 1;
            m = m * m;
        }
        return res;
    }

    Mat E;//转移矩阵
    Mat f;
    void init() {
        E.clear();
        E = {
                3, -1, 1,
                1, 0, 0,
                0, 0, 1
        };
        f = {
                12, 0, 0,
                4, 0, 0,
                1, 0, 0
        };
    }
}
using namespace Matrix;

int n;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    while (cin >> n) {
        init();
        if (n == 1) cout << 4 << endl; // k=1 对应 F[0]
        else if (n == 2) cout << 12 << endl;
        else {
            cout << (pow(E, n - 2) * f).mat[0][0] << endl;
        }
    }
    return 0;
}

6199 gems gems gems
6199 gems gems gems

6200 mustedge mustedge mustedge · lct

6200 mustedge mustedge mustedge

洛谷 P2542 [AHOI2005] 航线规划 的翻版:

不存在删边情况,

当有双连通分量出现,连通内部任意两个点之间的关键路径均为0(有环就有两条路可以走),所以可以将整个连通分量缩成一个点,

关键路径数 = 缩点后剩余点数 - 1

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
namespace LCT { // lct板子

    int h[N];// 缩点后x所在的点 类似并查集的f[]数组

    const int mod = 51061;
    int w[N];// w[x]表示x的点权

    int sz[N];//sz[x] 表示以x为根的splay树的大小
    int rev[N];//rev为区间翻转懒标记数组
    int f[N];// 父节点
    int st[N];//栈
    int c[N][2];// 每个节点的两个儿子

    // 判断节点x是否为一个splay的根 如果不是根 返回true
    inline bool nRoot(register int x) {
        // 如果节点x是根 则其与其父亲节点之间连的是轻边
        // 如果连的是轻边 他的父亲的儿子里没有它
        return c[f[x]][0] == x || c[f[x]][1] == x;
    }

#define lc c[x][0]
#define rc c[x][1]

    // splay区间翻转操作
    inline void reverse(register int x) {
        swap(lc, rc);
        rev[x] ^= 1;
    }
    
    // 判断并释放懒标记
    inline void pushDown(register int x) {
        if (rev[x]) {
            swap(lc, rc);
            rev[lc] ^= 1, rev[rc] ^= 1;
            rev[x] = 0;
        }
    }

    // 更新信息
    inline void pushUp(register int x) {
        w[x] = w[lc] + w[rc] + 1;
    }

    // 一次旋转
    inline void rotate(register int x) {
        register int y = f[x], z = f[y], k = (c[y][1] == x), w = c[x][!k];
        if (nRoot(y))
            c[z][c[z][1] == y] = x;
        c[x][!k] = y;
        c[y][k] = w;

        if (w)
            f[w] = y;
        f[y] = x;
        f[x] = z;
        pushUp(y);//原来的父亲现在是儿子了 更新新儿子的信息 而新父亲的信息不是在这里更新的
    }

    // 旋转一颗splay树 改为以x为根节点
    inline void splay(register int x) {
        register int y = x, z = 0;
        st[++z] = y;//暂存当前点到根的整条路径 pushdown时一定要从上往下放标记
        while (nRoot(y)) st[++z] = y = f[y];
        while (z) pushDown(st[z--]);
        //然后再从下往上旋转 splay的基本操作
        while (nRoot(x)) {
            y = f[x];
            z = f[y];
            if (nRoot(y))
                rotate((c[y][0] == x) ^ (c[z][0] == y) ? x : y);
            rotate(x);
        }
        pushUp(x);//更新最终新父亲x的信息
    }

    inline int geth(register int x) {
        return x == h[x] ? x : h[x] = geth(h[x]);
    }

    // 打通当前根节点到指定节点的树链
    // 使得一条中序遍历以根开始、以指定点结束的splay出现
    inline void access(register int x) {
        for (int y = 0; x; y = x, x = f[x] = geth(f[x])) {
            splay(x);
            rc = y;
            pushUp(x);
        }
    }

    //换根
    inline void makeRoot(register int x) {
        access(x);
        splay(x);
        rev[x] ^= 1;
    }

    //提取路径 拉出x->y的路径成为一个splay 然后以y为splay的根
    inline void split(register int x, register int y) {
        makeRoot(x);
        access(y);
        splay(y);
    }

    // 查找在树的根
    // 因为lct要求满足中序遍历Splay得到的每个点的深度序列严格递增 根的深度最小
    // 一直往左儿子就是了
    int findRoot(register int x) {
        access(x);
        splay(x);
        while (lc) {
            pushDown(x);
            x = lc;
        }
        // 现在x是root
        splay(x);
        return x;
    }

    // 连边
    inline void link(register int x, register int y) {
        makeRoot(x);
        if (findRoot(y) != x) {
            f[x] = y;
        }
    }

    // 断边
    inline void cut(register int x, register int y) {
        makeRoot(x);
        if (findRoot(y) == x && f[y] == x && !c[y][0]) {
            f[y] = c[x][1] = 0;
            pushUp(x);
        }
    }

    // 判断 x 和 y 是否连通
    bool isConnected(int x, int y) {
        return findRoot(x) == findRoot(y);
    }

    inline void del(register int x, register int y) {
        if (x) {
            h[x] = y;
            del(lc, y);
            del(rc, y);
        }
    }

    //双连通分量存在时 连接x和y
    inline void merge(register int x, register int y) {
        if (x == y) return;
        makeRoot(x);
        if (findRoot(y) != x) {
            f[x] = y;// 等于 link(x,y)
            return;
        }
        // 双连通分量 暴力缩点
        del(rc, x);// 将以x为根的splay树缩成一个点 都用x来表示
        rc = 0;
        pushUp(x);//缩点 删点
    }

    void init(int n) {
        for (int i = 0; i <= n; i++) {
            f[i] = c[i][0] = c[i][1] = 0;
            w[i] = 1;
            h[i] = i;
        }
    }
}
using namespace LCT;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T, u, v, q, op;
    cin >> T;
    for (int cs = 1; cs <= T; cs++) {
        cout << "Case #" << cs << ":\n";
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            f[i] = c[i][0] = c[i][1] = 0;
            w[i] = 1, h[i] = i;
        }
        for (int i = 1; i <= m; i++) {
            cin >> u >> v;
            merge(geth(u), geth(v));
        }
        cin >> q;
        while (q--) {
            cin >> op >> u >> v;
            if (op == 1) {
                merge(geth(u), geth(v));
            } else {
                u = geth(u), v = geth(v);
                split(u, v);
                cout << w[v] - 1 << endl;
            }
        }
    }
    return 0;
}

6201 transaction transaction transaction 树形dp + 换根

hdu 6201 transaction transaction transaction

取树上某两点为起点和终点,问这条树链的权值最大是多少

只想到了树形dp+换根:根节点为终点,在树里找最小值所在对应的起点

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = (1ll << 45);
const int N = 2e5 + 10;
ll a[N], d[N];
int n;
struct Edge {
    int v;
    ll w;
};
vector<Edge> e[N];
ll dp[N][2];//dp[i][0] dp[i][1] 表示以节点i为根的子树里的最小花费 和 次小花费 不包含在i点买书的消费
int son[N][2];// dp[i][0] dp[i][1] 对应的儿子节点

void dfs1(int u, int f, ll dep) {
    d[u] = dep;

    for (auto x:e[u]) {
        int v = x.v;
        if (v != f) {
            dfs1(v, u, dep + x.w);

            ll tmp = -dep - x.w - a[v]; //dp[v][0]里不包含在v点买书的情况 所以要补上
            tmp = max(tmp, dp[v][0]);

            if (tmp >= dp[u][0]) {
                dp[u][1] = dp[u][0];
                son[u][1] = son[u][0];

                dp[u][0] = tmp;
                son[u][0] = v;
            } else if (tmp > dp[u][1]) {
                dp[u][1] = tmp;
                son[u][1] = v;
            }
        }
    }
}

ll ans[N];

//换根
void dfs2(int u, int f, ll Max) {
    Max = max(Max, -a[u]);// u方向传来的最小花费 Max必定为负值

    for (auto x:e[u]) {
        int v = x.v;
        if (v != f) {//以v为出售点 即 以v为新的根
            ll tmp1 = Max - x.w;// 根节点方向传来的最小花费
            ll tmp2 = son[u][0] == v ?
                      dp[u][1] + d[u] - x.w : dp[u][0] + d[u] - x.w;// u的子树方向传来的最小花费 
            // 特判最小花费既在u的子树里 又在v的子树里
            ll tmp3 = dp[v][0] + d[v];// v的子树里传来的最小花费
            ans[v] = a[v] + max(tmp1, max(tmp2, tmp3));
            dfs2(v, u, max(tmp1, tmp2)); //更新Max = Max-x.w = max(tmp1, tmp2)
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    dp[0][0] = dp[0][1] = -inf;

    int T;
    cin >> T;
    for (int cs = 1; cs <= T; cs++) {

        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            dp[i][0] = dp[i][1] = -inf;
            son[i][0] = son[i][1] = 0;
        }
        for (int i = 1, u, v, w; i < n; i++) {
            cin >> u >> v >> w;
            e[u].push_back({v, w});
            e[v].push_back({u, w});
        }
        dfs1(1, 0, 0);
        ll res = a[1] + dp[1][0];
        dfs2(1, 0, -a[1]);//初始时 最小花费为节点1买书的消费
        for (int i = 2; i <= n; i++) {
            res = max(res, ans[i]);
        }
        res = max(res, 0ll);
        cout << res << endl;

        for (int i = 1; i <= n; i++) {
            e[i].clear();
        }
    }
    return 0;
}

6202 cube cube cube
6202 cube cube cube

6203 ping ping ping · lca + 树链剖分

hdu 6203 ping ping ping

// 未ac代码 -  hdu 瘫了 
#include <bits/stdc++.h>
using namespace std;
template<class T>
void _print(T arg) {
    cout << arg << " ";
}

template<class... Args>
void log(Args... args) {
    int arr[] = {(_print(args), 0)...};
    cout << endl;
}

#define between(x, a, b) (a<=x && x<=b)
typedef unsigned long long ull;
typedef long long ll;

const int N = 1e4 + 10;
const int M = 1e5 + 10;
vector<int> e[N];
int n, m, k;
int dep[N];
namespace LCA {
    int Log[N], fa[N][20];

    void initLog() {
        Log[0] = -1, Log[1] = 0;
        for (int i = 2; i < N; i++) {
            Log[i] = Log[i / 2] + 1;
        }
    }

    void dfs(int u, int f) {
        dep[u] = dep[f] + 1;
        fa[u][0] = f;
        for (int i = 1; (1 << i) <= n; i++) {
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        }
        for (int v:e[u]) {
            if (v != f) {
                dfs(v, u);
            }
        }
    }

    int lca(int u, int v) {
        if (dep[u] < dep[v]) swap(u, v);
        while (dep[u] > dep[v])
            u = fa[u][Log[dep[u] - dep[v]]];
        if (u == v) return v;
        for (int i = Log[dep[u]]; i >= 0; i--) {
            if (fa[u][i] != fa[v][i]) {
                u = fa[u][i];
                v = fa[v][i];
            }
        }
        return fa[u][0];
    }

}
using namespace LCA;

struct segTree {
#define ls (o<<1)
#define rs (o<<1|1)
    struct node {
        int l, r;
        int lazy;
    } t[N << 2];

    void build(int o, int l, int r) {
        t[o] = {l, r, 0};
        if (l >= r) return;
        int mid = l + r >> 1;
        build(ls, l, mid);
        build(rs, mid + 1, r);
    }

    int query(int o, int L, int R) {
        if (L <= t[o].l && t[o].r <= R) {
            return t[o].lazy;
        }
        int mid = t[o].l + t[o].r >> 1;
        if (L > mid) return query(rs, L, R);
        else if (R <= mid) return query(ls, L, R);
        else return query(ls, L, mid) + query(rs, mid + 1, R);
    }

    void pushUp(int o) {
        t[o].lazy = t[ls].lazy + t[rs].lazy;
    }

    void update(int o, int pos) {
        if (t[o].l == t[o].r) {
            t[o].lazy = 1;
            return;
        }
        int mid = t[o].l + t[o].r >> 1;
        if (pos <= mid) update(ls, pos);
        else update(rs, pos);
        pushUp(o);
    }

} ST;

namespace TreeChain {
    int f[N], son[N], sz[N];

    void dfs1(int u, int fa) {
        f[u] = fa;
        //dep[u] = dep[fa] + 1;
        sz[u] = 1;
        son[u] = 0;
        for (int v:e[u]) {
            if (v != fa) {
                dfs1(v, u);
                sz[u] += sz[v];
                if (sz[v] > sz[son[u]]) {
                    son[u] = v;
                }
            }
        }
    }

    int dfn = 0, id[N], top[N];

    void dfs2(int u, int tp) {
        id[u] = ++dfn;
        top[u] = tp;
        if (son[u]) dfs2(son[u], tp);
        for (int v:e[u]) {
            if (v != f[u] && v != son[u]) {
                dfs2(v, v);
            }
        }
    }

    void init() {
        dfn = 0;
    }

    //判断树链上是否有标记过的点
    bool isExitence(int u, int v) {
        while (top[u] != top[v]) {
            if (dep[top[u]] < dep[top[v]]) swap(u, v);
            if (ST.query(1, id[top[u]], id[u])) return true;
            u = f[top[u]];
        }
        if (dep[u] > dep[v]) swap(u, v);
        return ST.query(1, id[u], id[v]) != 0;
    }
}
using namespace TreeChain;

struct Que {
    int u, v, fa;// f=lca(u,v)

    void input() {
        cin >> u >> v;
        u++, v++;
        fa = lca(u, v);
    }

    bool operator<(const Que b) {
        return dep[fa] > dep[b.fa];
    }
} a[M];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
    freopen("debug.out", "w", stdout);
#endif

    initLog();
    while (cin >> n) {
        for (int i = 1, u, v; i <= n; i++) {
            cin >> u >> v;
            u++, v++;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        n++;
        // lca init
        dfs(1, 0);
        // TreeChain init
        dfn = 0;
        dfs1(1, 0);
        dfs2(1, 1);

        cin >> m;
        for (int i = 1; i <= m; i++) {
            a[i].input();
        }
        sort(a + 1, a + 1 + m);

        ST.build(1, 1, n);
        int res = 0;
        for (int i = 1; i <= m; i++) {
            int u = a[i].u, v = a[i].v;
            if (isExitence(u, v)) continue;
            ST.update(1, id[a[i].fa]);
            res++;
        }
        cout << res << endl;

        for (int i = 0; i <= n; i++) {
            e[i].clear();
        }
    }
    return 0;
}

6204 triangulation triangulation triangulation
6204 triangulation triangulation triangulation

6205 card card card

hdu 6205 card card card

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 5;
typedef long long ll;
int a[MAXN];
int b[MAXN], c[MAXN];

int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++)scanf("%d", &b[i]);
        for (int i = 1; i <= n; i++)c[i] = a[i] - b[i];
        int id = 0;
        for (int i = n + 1; i <= 2 * n; i++) {
            a[i] = a[++id];
            b[i] = b[id];
            c[i] = c[id];
        }
        int sum = 0;
        int maxn = 0;
        int ansmaxn = 0;
        int ansl = 0;
        int l = 1;
        for (int i = 1; i <= 2 * n; i++) {
            maxn += a[i];
            sum += c[i];
            if (sum == 0) {
                if (ansmaxn < maxn) {
                    ansmaxn = maxn;
                    ansl = l;
                }
            }
            if (sum < 0) {
                sum = 0;
                maxn = 0;
                l = i + 1;
            }
        }
        printf("%d\n", l - 1);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值