2016 ACM/ICPC亚洲区沈阳站

A B C D E F G H I J K L M
O O O $\varnothing$ $\varnothing$   $\varnothing$ $\varnothing$ $\varnothing$ $\varnothing$      $\varnothing$

 

[A. Thickest Burger]

签到。

 

[B. Relative atomic mass]

签到

 

[C. Recursive sequence]

$$f[i] = f[i - 1] + 2 * f[i - 2] + i ^ 4$$

$$
\left[
\begin{matrix}
1 & 2 & 1 & 0 & 0 & 0 & 0 \\
1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 &0 & 1 & 4 & 6 &4 & 1 \\
0 & 0 & 0 & 1 & 3 & 3 & 1 \\
0 & 0 & 0 & 0 & 1 & 2 & 1 \\
0 & 0 & 0 & 0 & 0 & 1 & 1 \\
0 & 0 & 0 & 0 & 0 & 0 & 1
\end{matrix}
\right]
\times
\left[
\begin{matrix}
f_{i-1} & 0 & 0 & 0 & 0 & 0 & 0 \\
f_{i-2} & 0 & 0 & 0 & 0 & 0 & 0\\
i^4 &0 & 0 & 0& 0 &0 & 0 \\
i^3 & 0 & 0 & 0 & 0 & 0 & 0 \\
i^2 & 0 & 0 & 0 & 0 & 0 & 0\\
i & 0 & 0 & 0 & 0 & 0 & 0 \\
1 & 0 & 0 & 0 & 0 & 0 & 0
\end{matrix}
\right]=
\left[
\begin{matrix}
f_{i} & 0 & 0 & 0 & 0 & 0 & 0 \\
f_{i} & 0 & 0 & 0 & 0 & 0 & 0\\
(i+1)^4 &0 & 0 & 0& 0 &0 & 0 \\
(i+1)^3 & 0 & 0 & 0 & 0 & 0 & 0 \\
(i+1)^2 & 0 & 0 & 0 & 0 & 0 & 0\\
i+1 & 0 & 0 & 0 & 0 & 0 & 0 \\
1 & 0 & 0 & 0 & 0 & 0 & 0
\end{matrix}
\right]
$$

#include <bits/stdc++.h>
#define ll long long

const ll MOD = 2147493647;
const int N = 7;

struct Mat {
    ll a[10][10];
    Mat() {
        memset(a, 0, sizeof(a));
    }
    Mat(int x) {
        memset(a, 0, sizeof(a));
        for (int i = 1; i <= N; i++)
            a[i][i] = 1;
    }
    Mat operator * (const Mat &rhs) const {
        Mat c;
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= N; j++)
                for (int k = 1; k <= N; k++)
                    c.a[i][j] = (c.a[i][j] + a[i][k] * rhs.a[k][j] % MOD) % MOD;
        return c;
    }
};

Mat qp(Mat a, int b) {
    Mat c(1);
    while (b) {
        if (b & 1) c = a * c;
        a = a * a;
        b >>= 1;
    }
    return c;
}

int main() {
    int T;
    scanf("%d", &T);
    Mat base;
    base.a[1][1] = 1; base.a[1][2] = 2; base.a[1][3] = 1;
    base.a[2][1] = 1;
    base.a[3][3] = 1; base.a[3][4] = 4; base.a[3][5] = 6; base.a[3][6] = 4; base.a[3][7] = 1;
    base.a[4][4] = 1; base.a[4][5] = 3; base.a[4][6] = 3; base.a[4][7] = 1;
    base.a[5][5] = 1; base.a[5][6] = 2; base.a[5][7] = 1;
    base.a[6][6] = 1; base.a[6][7] = 1;
    base.a[7][7] = 1;
    while (T--) {
        int n, a, b;
        scanf("%d%d%d", &n, &a, &b);
        if (n == 1) {
            printf("%d\n", a);
            continue;
        }
        if (n == 2) {
            printf("%d\n", b);
            continue;
        }
        n--;
        n--;
        Mat ans;
        ans.a[1][1] = b; ans.a[2][1] = a; ans.a[3][1] = 81; ans.a[4][1] = 27; ans.a[5][1] = 9; ans.a[6][1] = 3; ans.a[7][1] = 1;
        ans = qp(base, n) * ans;
        printf("%lld\n", ans.a[1][1] % MOD);
    }
    return 0;
}
View Code

 

[D. Winning an Auction]

博弈。

$dp[n][a][b]$ 表示当前剩下 $n$ 个物品,第一个人有 $a$ 元钱,第二个人有 $b$ 元钱,第一个人能获得的物品数。

$dp[1][a][b] = [a\leq b]$

消除奇偶性可以通过从 $dp[i - 1][b][a]$ 转移过来。这样就不用考虑奇偶了。

然后枚举两个人分别要出多少钱,当第一个人出 $x$ 块钱,第二个人出 $x+1$ 块钱会使第一个人的收益变小,那么第二个人会继续加价。同理第一个人会继续加价。当无法得到更好的收益的时候就停下来。

#include <cstdio>
#include <algorithm>
#include <cstring>

const int N = 256;
short dp[N][N][N];

int n, T, a, b;

inline int geta(int n, int a) {
    return n / 2 * a + (n - n / 2) * (a + 1);
}

int main() {
    for (int a = 0; a < N; a++)
        for (int b = 0; b < N; b++)
            if (a >= b) dp[1][a][b] = 1;
    for (int i = 2; i < N; i++) {
        for (int a = 0; a < N; a++) {
            int limit = std::min(N, geta(i, a));
            for (int b = 0; b < limit; b++) {
                if (a == b) {
                    dp[i][a][b] = (i + 1) / 2;
                    continue;
                }
                int vala = i - dp[i - 1][b][a];
                int valb = 0;
                for (int u = 1; ; u++) {
                    if (b < u || (valb = (i - 1 - dp[i - 1][b - u][a])) >= vala) {
                        dp[i][a][b] = vala;
                        break;
                    } 
                    if (a < u || (vala = (i - dp[i - 1][b][a - u])) <= valb) {
                        dp[i][a][b] = valb;
                        break;
                    }
                }
            }
        }
    }
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &a, &b);
        int ans1 = dp[n][a][b], ans2 = n - ans1;
        printf("Alice %d Bob %d\n", ans1, ans2);
    }
    return 0;
}
View Code

 

[E. Counting Cliques]

经过暑假牛客多校的洗礼,看到团就想到暴搜...

首先用了bfs+bitset。T了。

改成vector判,又T了。

看了一份题解是dfs,存团的是数组。

我把那份代码的数组改成vector,还是T。

这么卡STL的吗...

#include <bits/stdc++.h>

const int N = 107;
std::vector<int> G[N];
bool mp[N][N];

int ans, n, m, s;

void solve(int *a, int u) {
    if (a[0] == s) {
        ans++;
        return;
    }
    for (int v: G[u]) {
        if (v <= u) continue;
        bool flag = 1;
        for (int j = 1; j <= a[0]; j++) {
            if (!mp[a[j]][v]) {
                flag = 0;
                break;
            }
        }
        if (!flag) continue;
        a[++a[0]] = v;
        solve(a, v);
        --a[0];
    }
}

int a[N];

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &m, &s);
        for (int i = 0; i <= n; i++) {
            G[i].clear();
            for (int j = 0; j <= n; j++)
                mp[i][j] = 0;
            a[i] = 0;
        }
        for (int i = 0; i < m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            if (u > v) std::swap(u, v);
            G[u].push_back(v);
            mp[u][v] = mp[v][u] = 1;
        }
        ans = 0;
        for (int i = 1; i <= n; i++) {
            a[0] = 1;
            a[1] = i;
            solve(a, i);
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 

[G. Do not pour out]

积分题。

看的这篇 https://www.cnblogs.com/chen9510/p/7635679.html

#include <bits/stdc++.h>

const double pi = acos(-1.0);
const double eps = 1e-10;

inline int dcmp(double x) {
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}

double cal(double x) {
    return sin(x) - x * cos(x) - 1 / 3.0 * sin(x) * sin(x) * sin(x);
}

double calV(double mid) {
    double V = cal(acos(1.0)) - cal(acos(1.0 - mid));
    V *= -2.0 / mid;
    return V;
}

double cal2(double x) {
    return x + sin(2 * x) / 2;
}

double area(double a, double x) {
    return (cal2(pi / 2.0) - cal2(asin(x / a))) * a;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        double d;
        scanf("%lf", &d);
        if (dcmp(d - 1) >= 0) {
            double h = 4 - 2 * d;
            double a = sqrt(2 * 2 + h * h) / 2;
            printf("%.5f\n", pi * a);
            continue;
        }
        if (dcmp(d) == 0) {
            puts("0.00000");
            continue;
        }
        double l = 0, r = 2.0;
        for (int i = 0; i < 50; i++) {
            double mid = (l + r) / 2.0;
            double V = calV(mid);
            if (dcmp(V - pi * d) == 0) break;
            if (dcmp(V - pi * d) < 0) l = mid;
            else r = mid;
        }
        double mid = l;

        int flag = 1;
        if (mid < 1) flag = -1;
        double len = sqrt(mid * mid + 4);
        double h = sqrt(1 - (1 - mid) * (1 - mid));
        double a = len / (1 + flag * sqrt(1 - h * h));
        double x = a - len;
        printf("%.5f\n", area(a, x));
    }
}
View Code

 

[H. Guessing the Dice Roll]

先把AC自动机建出来。在AC自动机上DP。

$dp[i] = \sum \frac{1}{6} \times dp[from]$

$from$ 为能转到 $i$ 且不为某个串的结尾的结点

因为会存在环,所以高斯消元就行了。

#include <bits/stdc++.h>

const int N = 100 + 7;

double mat[N][N];
const double eps = 1e-8;

inline void gauss(int n) {
    for(int i = 0; i <= n; i++) {
        int r = i;
        for(int j = i + 1; j <= n; j++)
            if(std::fabs(mat[r][i]) < std::fabs(mat[j][i])) 
                r = j;
        if(r != i) std::swap(mat[i], mat[r]);
        for(int j = 0; j <= n; j++) {
            if(j == i) continue;
            double t = mat[j][i] / mat[i][i];
            for(int k = i; k <= n + 1; k++)
                mat[j][k] -= mat[i][k] * t;
        }
    }
    for(int i = 1; i <= n; i++) {
        mat[i][n + 1] /= mat[i][i];
    }
} 

struct Aho {
    static const int sz = 6;
    int ch[N][sz], last[N], fail[N], tol;
    bool end[N];
    void init() {
        tol = 0;
        newnode();
    }
    inline int newnode() {
        memset(ch[tol], 0, sizeof(ch[tol]));
        last[tol] = fail[tol] = end[tol] = 0;
        return tol++;
    }
    void insert(int *a, int n) {
        int u = 0;
        for (int i = 0; i < n; i++) {
            int id = a[i] - 1;
            if (!ch[u][id]) ch[u][id] = newnode();
            u = ch[u][id];
        }
        end[u] = 1;
    }
    void build() {
        std::queue<int> que;
        for (int i = 0; i < sz; i++)
            if (ch[0][i]) que.push(ch[0][i]), fail[ch[0][i]] = last[ch[0][i]] = 0;
        while (!que.empty()) {
            int u = que.front(); que.pop();
            end[u] |= end[last[u]];
            for (int i = 0; i < sz; i++) {
                int &v = ch[u][i];
                if (v) {
                    fail[v] = ch[fail[u]][i];
                    que.push(v);
                    last[v] = end[fail[v]] ? fail[v] : last[fail[v]];
                } else {
                    v = ch[fail[u]][i];
                }
            }
        }
    }
    void solve() {
        memset(mat, 0, sizeof(mat));
        mat[0][tol] = -1.0;
        for (int i = 0; i < tol; i++) {
            mat[i][i] = -1.0;
            if (end[i]) continue;
            for (int j = 0; j < sz; j++)
                mat[ch[i][j]][i] += 1.0 / 6;
        }
        gauss(tol - 1);
        bool flag = 0;
        for (int i = 0; i < tol; i++)
            if (end[i]) {
                if (flag) putchar(' ');
                printf("%.6f", mat[i][tol]);
                flag = 1;
            }
        puts("");
    }
} ac;

int a[20];

int main() {
    int T;
    scanf("%d", &T);
    for (; T--; ) {
        int n, l;
        scanf("%d%d", &n, &l);
        ac.init();
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < l; j++)
                scanf("%d", a + j);
            ac.insert(a, l);
        }
        ac.build();
        ac.solve();
    }
    return 0;
}
View Code

 

[I. The Elder]

$dp[u] = min(dp[anc] + (sum[u] - sum[anc])^2 + p)$

$anc$ 为 $u$ 到根的路径上的结点。

斜率优化DP一下。在进入一个结点时存储一下对当前队列的修改,离开一个结点时改回去,这样就能保证进入一个结点时,队列存储的都是它的祖先。

#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
#define fi first
#define se second

const int N = 1e5 + 7;
const double eps = 1e-10;
ll dp[N];
int n, p, que[N], l, r;
ll sum[N];
std::vector<std::pii> G[N];

inline ll sqr(ll x) {
    return x * x;
}

inline ll up(int i, int j) {
    return dp[i] + sqr(sum[i]) - dp[j] - sqr(sum[j]);
}

inline ll down(int i, int j) {
    return sum[i] - sum[j];
}

ll ans;

void dfs(int u, int fa = 0) {
    std::vector<std::pii> vec;
    int x = l, y = r;
    while (l < r && up(que[l + 1], que[l]) <= down(que[l + 1], que[l]) * 2 * sum[u]) {
        vec.push_back(std::pii(l, que[l]));
        l++;
    }
    if (u != 1) {
        dp[u] = dp[que[l]] + sqr(sum[u] - sum[que[l]]) + p;
        ans = std::max(ans, dp[u]);
    }
    while (l < r && up(que[r], que[r - 1]) * down(u, que[r]) >= up(u, que[r]) * down(que[r], que[r - 1])) {
        vec.push_back(std::pii(r, que[r]));
        r--;
    }
    que[++r] = u;
    for (auto v: G[u]) {
        if (v.fi == fa) continue;
        sum[v.fi] = sum[u] + v.se;
        dfs(v.fi, u);
    }
    l = x, r = y;
    for (auto p: vec)
        que[p.fi] = p.se;
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &p);
        for (int i = 1; i <= n; i++) 
            dp[i] = sum[i] = 0, G[i].clear();
        for (int i = 1, u, v, w; i < n; i++) {
            scanf("%d%d%d", &u, &v, &w);
            G[u].push_back(std::pii(v, w));
            G[v].push_back(std::pii(u, w));
        }
        dp[1] = -p;
        que[l = r = 1] = 0;
        ans = 0;
        dfs(1);
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

 

[J. Query on a graph]

基环树先找出环,用一个数组 $a$ 记录位置。然后以环上的每个点为根,去bfs非环上的点得到bfs序.

那么对于非环上的点,与他距离不大于 $k$ 的点bfs序连续,对于环上的点,与他距离不大于 $k$ 的点在 $a$ 数组里连续

这样就可以用线段树维护了。

然后就是恶心的细节了。

#include <bits/stdc++.h>
#define ll long long

inline void checkmax(int &a, int b) {
    if (a < b) a = b;
}

inline void checkmin(int &a, int b) {
    if (a > b) a = b;
}

const int N = 1e5 + 7;
std::vector<int> vec[N];
int fa[N], son[N], n, id[N], BCC[N], a[N];
int tol;
bool vis[N];

void getBCC(int u, int v) {
    for (int i = u; i != v; i = fa[i]) 
        a[BCC[i] = ++a[0]] = i;
    a[BCC[v] = ++a[0]] = v;
}

void dfs(int u, int pre) {
    vis[u] = 1;
    for (int v: vec[u]) {
        if (v == pre) continue;
        if (!vis[v]) {
            fa[v] = u;
            dfs(v, u);
        } else if (!BCC[u]) {
            getBCC(u, v);
        }
    }
}

int que[N];
int ls[N], rs[N], lg[N], rg[N];

void bfs(int s) {
    int h = 0, t = 0;
    que[++t] = s;
    id[s] = ++tol;
    while (h != t) {
        int u = que[++h];  ls[u] = tol + 1;
        for (int v: vec[u])
            if (!id[v] && !BCC[v]) {
                fa[v] = u;
                que[++t] = v;
                id[v] = ++tol;
            }
        rs[u] = tol;
    }
    for (int i = 1; i <= t; i++) {
        int u = que[i]; 
        lg[u] = N; rg[u] = -N;
        for (int v: vec[u])
            if (id[v] > id[u] && !BCC[v])
                checkmin(lg[u], ls[v]), checkmax(rg[u], rs[v]);
    }
}

struct Seg {
    #define lp p << 1
    #define rp p << 1 | 1
    static const int NN = N * 4;
    ll sum[NN], lazy[NN];
    void build(int p, int l, int r) {
        sum[p] = lazy[p] = 0;
        if (l == r) return;
        int mid = l + r >> 1;
        build(lp, l, mid);
        build(rp, mid + 1, r);
    }
    inline void pushup(int p) {
        sum[p] = sum[lp] + sum[rp];
    }
    inline void tag(int p, int len, ll w) {
        lazy[p] += w;
        sum[p] += len * w;
    }
    inline void pushdown(int p, int llen, int rlen) {
        if (lazy[p]) {
            tag(lp, llen, lazy[p]);
            tag(rp, rlen, lazy[p]);
            lazy[p] = 0;
        }
    }
    void update(int p, int l, int r, int x, int y, int w) {
        if (x > r || l > y) return;
        if (x <= l && y >= r) {
            tag(p, r - l + 1, w);
            return;
        }
        int mid = l + r >> 1;
        pushdown(p, mid - l + 1, r - mid);
        if (x <= mid) update(lp, l, mid, x, y, w);
        if (y > mid) update(rp, mid + 1, r, x, y, w);
        pushup(p);
    }
    ll query(int p, int l, int r, int x, int y) {
        if (x > r || l > y) return 0;
        if (x <= l && y >= r) return sum[p];
        int mid = l + r >> 1;
        pushdown(p, mid - l + 1, r - mid);
        ll ans = 0;
        if (x <= mid) ans += query(lp, l, mid, x, y);
        if (y > mid) ans += query(rp, mid + 1, r, x, y);
        return ans;
    }
    inline int getl(int x) { return (x > 1) ? x - 1 : a[0]; }
    inline int getr(int x) { return (x < a[0]) ? x + 1: 1; }
    inline void update(int u, int w) { update(1, 1, tol, u, u, w); }
    inline ll query(int u) { return query(1, 1, tol, u, u); }
    void update(int u, int k, int w) {
        update(id[u], w);
        if (k >= 1) {
            update(1, 1, tol, ls[u], rs[u], w);
            if (!BCC[u]) update(id[fa[u]], w);
            else {
                update(id[a[getl(BCC[u])]], w);
                update(id[a[getr(BCC[u])]], w);
            }
        }
        if (k >= 2) {
            update(1, 1, tol, lg[u], rg[u], w);
            if (!BCC[u]) {
                update(id[u], -w);
                update(1, 1, tol, ls[fa[u]], rs[fa[u]], w);
                int p = BCC[fa[u]];
                if (!p) update(id[fa[fa[u]]], w);
                else update(id[a[getl(p)]], w), update(id[a[getr(p)]], w);
            } else {
                int bl = getl(BCC[u]), br = getr(BCC[u]);
                update(1, 1, tol, ls[a[bl]], rs[a[bl]], w);
                update(1, 1, tol, ls[a[br]], rs[a[br]], w);
                if (getl(bl) != br) update(id[a[getl(bl)]], w);
                if (getr(br) != bl && getr(br) != getl(bl)) update(id[a[getr(br)]], w);
            }
        }
    }
    ll query(int u, int k) {
        ll ans = query(id[u]);
        if (k >= 1) {
            ans += query(1, 1, tol, ls[u], rs[u]);
            if (!BCC[u]) ans += query(id[fa[u]]);
            else ans += query(id[a[getl(BCC[u])]]) + query(id[a[getr(BCC[u])]]);
        }
        if (k >= 2) {
            ans += query(1, 1, tol, lg[u], rg[u]);
            if (!BCC[u]) {
                ans += query(1, 1, tol, ls[fa[u]], rs[fa[u]]) - query(id[u]);
                int p = BCC[fa[u]];
                if (!p) ans += query(id[fa[fa[u]]]);
                else ans += query(id[a[getl(p)]]) + query(id[a[getr(p)]]);
            } else {
                int bl = getl(BCC[u]), br = getr(BCC[u]);
                ans += query(1, 1, tol, ls[a[bl]], rs[a[bl]]) + query(1, 1, tol, ls[a[br]], rs[a[br]]);
                if (getl(bl) != br) ans += query(id[a[getl(bl)]]);
                if (getr(br) != bl && getr(br) != getl(bl)) ans += query(id[a[getr(br)]]);
            }
        }
        return ans;
    }
} seg;

int main() {
    freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        tol = a[0] = 0;
        for (int i = 1; i <= n; i++) {
            fa[i] = son[i] = BCC[i] = id[i] = vis[i] = 0;
            vec[i].clear();
        }
        for (int i = 1, u, v; i <= n; i++) {
            scanf("%d%d", &u, &v);
            vec[u].push_back(v);
            vec[v].push_back(u);
        }
        dfs(1, 0);
        for (int i = 1; i <= a[0]; i++)
            bfs(a[i]);
        seg.build(1, 1, tol);
        int q;
        scanf("%d", &q);
        while (q--) {
            int u, k;
            char s[20];
            scanf("%s%d%d", s, &u, &k);
            if (s[0] == 'M') {
                int d;
                scanf("%d", &d);
                seg.update(u, k, d);
            } else {
                printf("%lld\n", seg.query(u, k));
            }
        }
    }
    return 0;
}
View Code

 

[M. Subsequence]

终于把这道题给补了...比赛前我居然还在搞这些不考的东西

裸 K 短路,并且是有向无环图,求 $T$ 到其他点的最短路拓扑排序就能解决。

求出 $T$ 到其他所有点的最短路树,记 $d[i]$ 为 $i$ 到 $T$ 的最短路。给每一个点分配一个前趋,如果多个相同则选其中一个。(注意有重边时要记录边而不是记录前趋的点!!!)

走 $S$ 到 $T$ 上的树边即为最短路,走一条非树边 $(u, v, c)$ 则会使费用增大 $d[v] + c - d[u]$。记该花费为非树边的费用,显然树边的费用为 $0$。

将一条从 $S$ 到 $T$ 的路径记为其经过的非树边序列 $p$,那么这条路径的权值和即为 $d[s] + \sum_{(u,v,c) \in p} (d[v] + c - d[u])$

求 $k$ 短路即求第 $k$ 小的合法非树边序列费用之和。

合法的非树边序列为相邻两条非树边 $e$,$f$,$e$ 在 $f$ 之前,$head(f)$ 需为 $tail(e)$ 在 $T$ 上的祖先或相同。

用一个堆来存储搜索的状态,当前的非树边权值和为优先级,再存储最后一条非树边的起点。

往后可以有两个决策,一为直接加上最后一条非树边的终点之后的非树边中,权值最小的那个。

二为将最后一条非树边替换为 $u$ 之后的非树边下一条比这条非树边大的。

发现需要用另一个堆维护每个点往后所有的非树边。发现 $u$ 和 $pre[u]$ 大部分非树边相同,只是多了一些以自身为起点的非树边,那么可以可持久化地添加非树边。

可以用可持久化左偏树,虽然论文里说的是它不可完全可持久化。

论文

还是挺好写的,就是在合并的过程中用新的节点来合并。像线段树合并。

#include <bits/stdc++.h>

#define pii pair<int, int>
#define fi first
#define se second

const int N = 4e5 + 7;
const int INF = 0x3f3f3f3f;
template<class T>inline bool chkmin(T &a, const T &b) { return a > b ? a = b, true : false; }
template<class T>inline bool chkmax(T &a, const T &b) { return a < b ? a = b, true : false; }

namespace Heap {
    struct Node {
        int lp, rp, v, dis, val;
    } tree[N * 20];
    int tol, root[N];
    int newnode(int x = 0, int y = 0) {
        int p = ++tol;
        tree[p].val = x, tree[p].v = y;
        tree[p].lp = tree[p].rp = 0; tree[p].dis = 1;
        return p;
    }
    int merge(int p, int q) {
        if (!p || !q) return p + q;
        if (tree[p].val < tree[q].val) std::swap(p, q);
        int x = newnode();
        tree[x] = tree[p];
        tree[x].rp = merge(tree[x].rp, q);
        if (tree[tree[x].lp].dis < tree[tree[x].rp].dis) std::swap(tree[x].lp, tree[x].rp);
        tree[x].dis = tree[tree[x].rp].dis + 1;
        return x;
    }
    inline void init() {
        tol = 0;
        tree[0].dis = -1;
    }
} using namespace Heap;

int n, k, cnt, id[N][2], s, t, deg[N];
int head1[N], head2[N], e, d[N];
struct Ed {
    int v, ne, c;
} E[N];

void add(int u, int v, int c) {
    E[cnt].v = v; E[cnt].ne = head1[u]; E[cnt].c = c; head1[u] = cnt++;
    E[cnt].v = u; E[cnt].ne = head2[v]; E[cnt].c = c; head2[v] = cnt++;
    deg[u]++;
}

int Q[N], pre[N];

int topo(int S, int *head) {
    int l = 0, r = 0;
    Q[r++] = S;
    d[S] = 0;
    while (l <= r) {
        int u = Q[l++];
        for (int i = head[u]; ~i; i = E[i].ne) {
            int v = E[i].v;
            if (chkmax(d[v], d[u] + E[i].c)) pre[v] = i;
            if (!--deg[v]) Q[r++] = v;
        }
    }
    return r;
}

int solve() {
    scanf("%d%d", &n, &k);
    cnt = e = 0;
    init();
    for (int i = 0; i <= n; i++)
        id[i][0] = ++e, id[i][1] = ++e;
    s = ++e, t = ++e;
    for (int i = 1; i <= e; i++) {
        root[i] = 0;
        d[i] = -INF;
        pre[i] = -1;
        deg[i] = 0;
        head1[i] = head2[i] = -1;
    }
    for (int i = 0; i < n; i++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(id[i][0], id[i + 1][c], a);
        add(id[i][1], id[i + 1][c], b);
        add(id[i][0], id[i + 1][0], 0);
        add(id[i][1], id[i + 1][1], 0);
    }
    add(s, id[0][0], 0);
    add(id[n][0], t, 0);
    add(id[n][1], t, 0);
    int m = topo(t, head2);
    if (k == 1) return d[s];
    for (int j = 0; j < m; j++) {
        int u = Q[j];
        for (int i = head1[u]; ~i; i = E[i].ne) {
            int v = E[i].v;
            if (d[v] == INF || (i ^ 1) == pre[u]) continue;
            root[u] = merge(root[u], newnode(d[v] + E[i].c - d[u], v));
        }
        if (~pre[u]) root[u] = merge(root[u], root[E[pre[u] ^ 1].v]);
    }
    std::priority_queue<std::pii> que;
    que.push(std::pii(d[s] + tree[root[s]].val, root[s]));
    for (k -= 2; k; k--) {
        auto p = que.top(); que.pop();
        int i = p.se, j = root[tree[i].v];
        if (j) que.push(std::pii(p.fi + tree[j].val, j));
        if (tree[i].lp) que.push(std::pii(p.fi - tree[i].val + tree[tree[i].lp].val, tree[i].lp));
        if (tree[i].rp) que.push(std::pii(p.fi - tree[i].val + tree[tree[i].rp].val, tree[i].rp));
    }
    return que.top().fi;
}

int main() {
    freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) printf("%d\n", solve());
    return 0;
}
View Code

 

posted @ 2019-10-17 00:28  Mrzdtz220  阅读(286)  评论(0)    收藏  举报