2016中国大学生程序设计竞赛

solved 5/11

2016中国大学生程序设计竞赛 - 网络选拔赛

大数取模 1001 A water problem(ZCJ&&BH)

题意:

  判断n%10001=0?n的长度<=10000000。

思路:

  这题不是我是AC的,但是看没人写就写点吧。大数取模,同余模定理,数字长度很长,用字符数组读入就好了。

代码:

#include <bits/stdc++.h>

const int N = 10000000 + 5;
char s[N];

int main() {
    int lcm = 10001;
    int cas = 0;
    while (scanf ("%s", s) == 1) {
        printf ("Case #%d: ", ++cas);
        int mod = 0;
        for (int i=0; s[i]; ++i) {
            mod = (mod * 10 + (s[i] - '0')) % lcm;
        }
        puts (!mod ? "YES" : "NO");
    }
    return 0;
}

数论+xor方程组消元 1002 Zhu and 772002(BH)

题意:

  给n个数,选择一些数字乘积为平方数的选择方案数。(《训练指南》书上的例题)。

分析:

  每一个数字分解质因数。比如4, 6, 10, 15,, 令表示选择第i个数字,那么,如果p是平方数,那么每个质因数上的指数为偶数,x1系数为2已经是偶数不考虑。可以转换为异或为0判断偶数,即奇数置为1,偶数置为0,然后n个数字m个质因数的增广矩阵消元看有几个自由变量(取0或1无所谓),答案是2^r - 1(全部都不取方案不算)

代码:

#include <bits/stdc++.h>

const int N = 300 + 5;
const int P = 2000 + 5;
const int MOD = 1000000007;
const double EPS = 1e-8;
typedef long long ll;
typedef int Matrix[N][N];

bool is_prime[P];
int prime[P/2];

void prime_table(int n) {
    int &c = prime[0] = 0;
    memset (is_prime, true, sizeof (is_prime));
    is_prime[0] = is_prime[1] = false;
    for (int i=2; i<=n; ++i) {
        if (is_prime[i]) {
            prime[++c] = i;
            for (int j=i*2; j<=n; j+=i) {
                is_prime[j] = false;
            }
        }
    }
}

Matrix A;

void count_factors(int id, ll n, int &m) {
    for (int i=1; i<=prime[0]; ++i) {
        if (n % prime[i] == 0) {
            m = std::max (m, i);
            int p = prime[i];
            while (n % prime[i] == 0) {
                n /= prime[i];
                A[i-1][id] ^= 1;
            }
        }
    }
}

//xor_elimination
//m个方程,n个变量,求矩阵的秩
int rank(Matrix A, int m, int n) {
    int i = 0, j = 0, k, r, u;
    //处理第i个方程,第j个变量
    while (i < m && j < n) {
        r = i;
        for (k=i; k<m; ++k) {
            if (A[k][j]) {
                r = k;
                break;
            }
        }
        if (A[r][j]) {
            if (r != i) {
                for (k=0; k<=n; ++k) {
                    std::swap (A[r][k], A[i][k]);
                }
            }
            //消元后第i行的第一个非0列式第j列,第u>i的第j列均为0
            for (u=i+1; u<m; ++u) {
                if (A[u][j]) {
                    for (k=i; k<=n; ++k) {
                        A[u][k] ^= A[i][k];
                    }
                }
            }
            i++;
        }
        j++;
    }
    return i;
}

ll pow_mod(int x, int n) {
    ll ret = 1;
    for (; n; n>>=1) {
        if (n & 1) ret = ret * x % MOD;
        x = (ll) x * x % MOD;
    }
    return ret;
}

int main() {
    prime_table (2000);
    int T, cas = 0;
    std::cin >> T;
    while (T--) {
        int n;
        std::cin >> n;
        memset (A, 0, sizeof (A));
        int m = 0;
        for (int i=0; i<n; ++i) {
            ll x;
            std::cin >> x;
            count_factors (i, x, m);
        }
        int r = rank (A, m, n); //用到前m个素数
        printf ("Case #%d:
", ++cas);
        std::cout << ((pow_mod (2, n - r)) - 1 + MOD) % MOD << '
';  //n-r个自由变量,解集非空,所以-1
    }
    return 0;
}

树形DP 1003 Magic boy Bi Luo with his excited tree(BH)

题意:

  有n个点的一棵树,点有价值,边有花费,问从一个节点出发能获得的最大价值(可以往返,价值只算一次,花费每次都算)。

思路:

  一类典型的树形DP,之前做过几题(题目1题目2),可惜做得少,不够系统。这类题都是两次DFS,第一次自底向上处理出节点往下的最优值,第二次自上向下,函数参数包含节点上面的最优值。dp[u][0]表示从u节点开始往它的子树下走,最终回到u的最大价值,dp[u][1]表示从u节点开始往它的子树下走,最终不回到u的最大价值,显然dp[u][1]>=dp[u][0]。如下图所示,红线表示往下,蓝线表示往上,dp[u][1]有一条最右边的红线

至于第二次DFS,根据往哪个儿子的子树走下去,更新最优的up1和up2即可。

代码:

#include <bits/stdc++.h>

const int N = 1e5 + 5;
struct Edge {
    int v, c;
};
std::vector<Edge> edges[N];
int val[N];
int son[N];
int ans[N];
int n;

int dp[N][2];

//得到dp[u][0/1]
void DFS(int u, int fa) {
    dp[u][0] = dp[u][1] = val[u];
    son[u] = -1;  //dp[u][1] 从哪个儿子出发不回来
    for (Edge &e: edges[u]) {
        if (e.v == fa) continue;
        DFS (e.v, u);
        int tmp1 = std::max (0, dp[e.v][0] - e.c * 2);
        int tmp2 = dp[u][0] + std::max (0, dp[e.v][1] - e.c);
        dp[u][0] += tmp1;
        dp[u][1] += tmp1;
        if (dp[u][1] < tmp2) {
            dp[u][1] = tmp2;
            son[u] = e.v;
        }
    }
}

//up1:从u出发,不进入u的子树,最终返回u的最优值,up2:最终不返回u的最优值
void DFS(int u, int fa, int up1, int up2) {
    ans[u] = std::max (dp[u][0]+up2, dp[u][1]+up1);
    int s = son[u];
    int sum1 = up1 + dp[u][0];
    int sum2 = up1 + dp[u][1];
    if (sum2 <= up2 + dp[u][0]) {
        sum2 = up2 + dp[u][0];
        s = -1;
    }
    for (int i=0; i<edges[u].size (); ++i) {
        Edge &e = edges[u][i];
        if (e.v == fa)
            continue;
    //类似第一个DFS的做法,也就是dp[u][0/1]的次优值
        if (e.v == s) {
            int new_up1 = up1 + val[u];
            int new_up2 = up2 + val[u];
            for (int j=0; j<edges[u].size (); ++j) {
                Edge &e2 = edges[u][j];
                if (e2.v == fa || e2.v == e.v)
                    continue;
                int tmp1 = std::max (0, dp[e2.v][0] - e2.c*2);
                int tmp2 = new_up1 + std::max (0, dp[e2.v][1] - e2.c);
                new_up1 += tmp1;
                new_up2 += tmp1;
                new_up2 = std::max (new_up2, tmp2);
            }
            new_up1 = std::max (0, new_up1 - e.c*2);
            new_up2 = std::max (0, new_up2 - e.c);
            DFS (e.v, u, new_up1, new_up2);
        } else {
      //sub:去掉sum1里面e.v的贡献
            int sub = std::max (0, dp[e.v][0] - e.c*2);
            int new_up1 = std::max (0, sum1 - sub - e.c*2);
            int new_up2 = std::max (0, sum2 - sub - e.c);
            DFS (e.v, u, new_up1, new_up2);
        }
    }
}

void solve(int cas) {
    DFS (1, 0);
    DFS (1, 0, 0, 0);

    printf ("Case #%d:
", cas);
    for (int i=1; i<=n; ++i) {
        printf ("%d
", ans[i]);
    }
}

int main() {
    int T;
    scanf ("%d", &T);
    for (int cas=1; cas<=T; ++cas) {
        scanf ("%d", &n);
        for (int i=1; i<=n; ++i)
            scanf ("%d", val+i);
        for (int i=1; i<=n; ++i)
            edges[i].clear ();
        for (int i=1; i<n; ++i) {
            int u, v, c;
            scanf ("%d%d%d", &u, &v, &c);
            edges[u].push_back ((Edge) {v, c});
            edges[v].push_back ((Edge) {u, c});
        }
        solve (cas);
    }
    return 0;
}

贪心 1004 Danganronpa(BH)  

题意:

  每个小朋友两件礼物,相邻的小朋友普通礼物不能相同,神秘礼物没限制,问最多能分给几个小朋友。

思路:

  考虑普通礼物,相邻的不同,取数量最多的两种礼物AB,分发为ABABAB。。。将剩余的放回优先队列里面(sort一下)直到已分发的数量小于剩余的数量。数据水得sum/2也能过。。。

代码:

#include <bits/stdc++.h>

int n;
int a[15];

bool cmp(int x, int y) {
    return x > y;
}

int solve() {
    if (n == 1) {
        return a[1] > 1;
    }
    int ret = 0, res = 0;
    for (int i=1; i<=n; ++i)
        res += a[i];
    int mx = res / 2;
    while (res > ret) {
        std::sort (a+1, a+1+n, cmp);
        int mn = std::min (a[1], a[2]);
        if ((res - mn * 2) <= ret + mn * 2) {
            int x = (res + ret) / 2;
            ret += x;
            break;
        } else {
            res -= mn * 2;
            a[1] -= mn;
            a[2] -= mn;
            ret += mn * 2;
        }
        if (res == ret)
            break;
    }
    return std::min (ret, mx);
}

int main() {
    int T;
    scanf ("%d", &T);
    for (int cas=1; cas<=T; ++cas) {
        scanf ("%d", &n);
        for (int i=1; i<=n; ++i) {
            scanf ("%d", a+i);
        }
        printf ("Case #%d: %d
", cas, solve ());
    }
    return 0;
}

水题 1011 Lweb and String(ZCJ)

原文地址:https://www.cnblogs.com/NEVERSTOPAC/p/5770947.html