[Contest]2017 ACM/ICPC Asia Regional Shenyang Online(01 03 07 09 10 11待补)

1001 string string string

题意

给定一个字符串$s$,求其中出现$k$次的子串的个数。

题解

后缀自动机。

代码


1002 cable cable cable

题意

给定$M$个显示屏和$K$个不同颜色的信号源。求最少连多少条数据线,可以使得任选$K$个显示屏,都可以恰好显示$K$个不同颜色。

题解

找规律。
考虑信号源$1$和显示屏$[1,M-K+1]$相连,信号源$2$和显示屏$[2,M-K+2]$相连……信号源$K$和显示屏$[K,M]$相连。故:$res=(M-K+1)*K$。

代码

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

1003 happy happy happy

题意

给定$n$个数$a_i$,爸爸和儿子玩游戏,爸爸每次可以选最左和最右的数,儿子先选且每次选最左最右中最大的(相同选最左),最终和最大的人获胜。爸爸想让儿子获胜,求最小差值。

题解

$DFS$。

代码


1004 array array array

题意

给定$n$个数$A_i$,判断能否去掉$k$个数使其变成不上升或不下降序列。

题解

$DP$。
求最长不下降和最长不上升子序列的长度,和$n-k$比较即可。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+9;
int n, k, a[N], b[N], d[N];

inline int read() {
    int s = 1, x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return s * x;
}

inline int dp(int c[N]) {
    int len = 0; 
    d[++len] = c[1];
    for (int i = 2; i <= n; i++) {
        if (c[i] >= d[len]) d[++len] = c[i];
        else *upper_bound(d + 1, d + len + 1, c[i]) = c[i];
    }
    return len;
}

int main() {
    int T = read();
    while (T--) {
        n = read(), k = read();
        for (int i = 1; i <= n; i++) a[i] = read();
        for (int i = 1; i <= n; i++) b[i] = -a[i];
        puts((dp(a) >= n - k || dp(b) >= n - k) ? "A is a magic array." : "A is not a magic array.");
    }
    return 0;
}

1005 number number number

题意

定义能用$k$项斐波拉契数列之和表示的数为“好数”,否则是“坏数”。给定$k$,求最小的“坏数”。

题解

找规律。
根据前几项我们发现:对于$k$,最小的坏数为$F_{2k+3}-1$。矩阵快速幂算一下即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3;
const int Q = 998244353;
struct Mat {
    ll mat[N][N];
}f, g;
int k;
Mat operator *(Mat a, Mat b) {
    Mat c; memset(c.mat, 0, sizeof(c.mat));
    for (int k = 1; k <= 2; k++) {
        for (int i = 1; i <= 2; i++) {
            for (int j = 1; j <= 2; j++) {
                c.mat[i][j] += (a.mat[i][k] % Q * b.mat[k][j] % Q);
                c.mat[i][j] %= Q;
            }
        }
    }
    return c;
}
Mat operator ^(Mat a, int n) {
    Mat c; memset(c.mat, 0, sizeof(c.mat));
    for (int i = 1; i <= 2; i++) {
        for (int j = 1; j <= 2; j++) {
            c.mat[i][j] = (i == j);
        }
    }
    for (; n; n >>= 1) {
        if (n & 1) c = c * a;
        a = a * a;
    }
    return c;
}
int main() {
    while (~scanf("%d", &k)) {
        memset(f.mat, 0, sizeof(f.mat));
        f.mat[1][1] = f.mat[1][2] = f.mat[2][1] = 1;
        g = f ^ (2 * k + 1);
        f.mat[1][2] = 0;
        g = g * f;
        printf("%d\n", g.mat[1][1] - 1);
    }
    return 0;
}

1006 gems gems gems

题意

给定$n$个价值为$V_i$的宝石,两个人从左往右取宝石,第一个人可以取$1$或$2$个,之后若一个人先取了$k$个,则另一个人只能取$k+1$个,当无法再取时游戏结束。求两个人所取宝石的最小差值。

题解

$DP$。
本来以为是个博弈论的,还是图样。。。
考虑动态规划,用$dp[0/1][i][j]$表示某一个人从第$i$个宝石开始取$j$或$j+1$个宝石的最优结果。
转移方程:
$\begin{equation}
dp[0][i][j]=\sum_{i}^{i+j-1}{V_i}+max
\begin{cases}
dp[1][i+j][j]\newline
dp[1][i+j+1][j+1]+V_{i+j} \newline
\end{cases}
\end{equation}$

$\begin{equation}
dp[1][i][j]=-\sum_{i}^{i+j-1}{V_i}+min
\begin{cases}
dp[0][i+j][j] \newline
dp[0][i+j+1][j+1]-V_{i+j} \newline
\end{cases}
\end{equation}$

考虑第三维的范围:极端情况第$k$轮取$k+1$个,有$\frac{(k+1)(k+2)}{2}< n$。故:第三维的$j\leq 200$。
这样时间复杂度为:$\Theta(400n)$。
下面来优化空间复杂度:
考虑不会超过$long\space long$,$dp$数组用$int$型即可;
考虑第二维转移有空间浪费,用滚动数组即可。
HDOJ是缺内存还是怎么滴啊。。$MLE$很难受啊。。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2e4+9;
const int K = 209;
const int Q = 255;
const int M = 255+9;
int sum[N], dp[2][M][K];

inline int read() {
    int s = 1, x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return s * x;
}

int main() {
    int T = read();
    while (T--) {
        int n = read();
        memset(sum, 0, sizeof(sum));
        for (int i = 1; i <= n; i++) {
            int v = read();
            sum[i] = sum[i - 1] + v;
        }
        memset(dp, 0, sizeof(dp));
        for (int i = n; i; i--) {
            for (int j = K - 1; j; j--) {
                if (i + j <= n) {
                    dp[0][i&Q][j] = sum[i+j-1] - sum[i-1] + max(dp[1][(i+j)&Q][j], dp[1][(i+j+1)&Q][j+1] + sum[i+j] - sum[i+j-1]);
                    dp[1][i&Q][j] = sum[i-1] - sum[i+j-1] + min(dp[0][(i+j)&Q][j], dp[0][(i+j+1)&Q][j+1] - sum[i+j] + sum[i+j-1]);
                    continue;
                }
                if (i + j - 1 <= n) {
                    dp[0][i&Q][j] = dp[1][(i+j)&Q][j] + sum[i+j-1] - sum[i-1];
                    dp[1][i&Q][j] = dp[0][(i+j)&Q][j] - sum[i+j-1] + sum[i-1];
                }
            }
        }
        printf("%d\n", dp[0][1][1]);
    }
}

1007 mustedge mustedge mustedge

题意

题解

代码


1008 transaction transaction transaction

题意

给定一个$n$个节点的树,每个点有一个点权$p_i$。选定两个节点$S$和$T$,使得【$p_T-p_S-$路径上的边权】最大,求这个最大值。

题解

树形$DP$。
考虑将$S$到$T$的路径拆成两部分即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5+9;
const int INF = 0x3f3f3f3f;
int T, n, p[N];
vector<pii> g[N];
ll res,dp[2][N];

inline int read() {
    int s = 1, x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return s * x;
}

inline void dfs(int u, int fa) {
    dp[0][u] = dp[1][u] = p[u];
    for (auto e : g[u]) {
        int v = e.first, w = e.second;
        if (v != fa) {
            dfs(v, u);
            dp[0][u] = max(dp[0][u], dp[0][v] - w);
            dp[1][u] = min(dp[1][u], dp[1][v] + w);
        }
    }
    res = max(res, dp[0][u] - dp[1][u]);
}

int main() {
    T = read();
    while (T--) {
        memset(p, 0, sizeof(p));
        n = read();
        for (int i = 1; i <= n; i++) p[i] = read();
        for (int i = 1; i <= n; i++) g[i].clear();
        for (int i = 1; i < n; i++) {
            int x = read(), y = read(), z = read();
            g[x].push_back( make_pair(y, z) );
            g[y].push_back( make_pair(x, z) );
        }
        res = 0; dfs(1, 0);
        printf("%lld\n", res);
    }
}

1009 cube cube cube

题意

题解

$DFS$。

代码


1010 ping ping ping

题意

题解

代码


1011 triangulation triangulation triangulation

题意

题解

代码


1012 card card card

题意

给定$n$堆数组成一个圈,每堆数有$a_i$和$b_i$两个参数。选一个堆数开始按顺序取,每次加上$a_i-b_i$直到和小于$0$停止。要求取数的$a_i$之和最大,求应该从哪一堆开始取。

题解

尺取法。
先将$n$堆数重复成$2n$堆数变成环,考虑我们只需要尺取$n$堆数。当$[i,j]$不满足时,即$\sum_{k=i}^{j}{a_k-b_k}< 0$,上式对$[i,j]$内的$i$都成立,因此我们只需要把$i$移动到$j+1$即可。时间复杂度是线性的。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2e6 + 9;
int n, a[N], b[N], c[N];

inline int read() {
    int s = 1, x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return s * x;
}

int main() {
    while (~scanf("%d", &n)) {
        for (int i = 0; i < n; i++) a[i] = read(), a[i + n] = a[i];
        for (int i = 0; i < n; i++) b[i] = read(), b[i + n] = b[i];
        for (int i = 0; i < n << 1; i++) c[i] = a[i] - b[i];
        int cur = 0, sum = 0, SUM = 0, res = 0;
        for (int i = 0, j = 0; j < n << 1; j++) {
            if (cur + c[j] < 0) {
                sum += a[j];
                if (sum > SUM) SUM = sum, res = i;
                cur = sum = 0, i = j + 1;
            } 
            else 
                cur += c[j], sum += a[j];
            if (j - i == n - 1 && sum > SUM) {
                SUM = sum, res = i;
                break;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}
posted @ 2017-09-11 16:17  jstztzy  阅读(118)  评论(0编辑  收藏  举报