CCNU 2016 个人排位赛 1

http://vjudge.net/contest/135602#overview
http://vjudge.net/contest/135602#rank

A

给出一些对 X 加加减减的过程,问 X 最后是几(X 最初是 0)。

#include <bits/stdc++.h>

int main()
{
    int n;
    scanf("%d", &n);
    int x = 0;
    char cmd[5];
    for (int i = 1; i <= n; ++i) {
        scanf("%s", cmd);
        if (cmd[1] == '-') {
            x--;
        }
        else {
            x++;
        }
    }
    printf("%d\n", x);
    return 0;
}

B

给出 x,每次减去 x 某个数位上的数,问最少多少次可以使得 x 变成 0。

可以 dp,如果注意到贪心策略也可以模拟通过。

#include <bits/stdc++.h>

const int MAX_N = 1e6 + 10;

int f[MAX_N];

int main()
{
    int n;
    scanf("%d", &n);
    f[0] = 0;
    for (int i = 1; i <= n; ++i) {
        f[i] = 1e9;
        for (int j = i; j; j /= 10) {
            if (j % 10 == 0) {
                continue;
            }
            f[i] = std::min(f[i], f[i - j % 10] + 1);
        }
    }
    printf("%d\n", f[n]);
    return 0;
}

C

给出一个字符串,查询一个区间中有多少对相邻相等的字符。

fi 表示 1 到 i 中相邻相等的字符对数,对于 l 到 r 的查询,答案就是 frfl

#include <bits/stdc++.h>

const int MAX_N = 1e5 + 10;

int f[MAX_N];
char str[MAX_N];

int main()
{
    scanf("%s", str + 1);
    int n = strlen(str + 1);
    f[1] = 0;
    for (int i = 2; i <= n; ++i) {
        f[i] = f[i - 1] + (str[i] == str[i - 1]);
    }
    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; ++i) {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d\n", f[r] - f[l]);
    }
    return 0;
}

D

n 个物品排列成 a(a > 1)行,n / a 列的方阵,每次取一行留下,其余丢掉,直到剩余一个物品即不再进行操作。问操作过程中每一步剩余物品数目的和最大是多少。

要上述值最大,应让操作更多,应让每次剩余的最多——这两个要求是统一的,即每次都使得 a 尽量小。故可以模拟这个过程,不难发现这个过程最多大概只有 O(n) 步,毋需担心时间。

#include <bits/stdc++.h>

int main()
{
    int n;
    std::cin >> n;
    long long ans = n;
    for (int i = 2; i <= n / i; ++i) {
        for (; n % i == 0; ) {
            n /= i;
            ans += n;
        }
    }
    if (n > 1) {
        ans += 1;
    }
    std::cout << ans << std::endl;
    return 0;
}

E

给出 x = 1,y = 0,依赖一个数列 a1,a2,...,an ,轮流进行题目指示的操作(比较繁琐……见题目描述吧……),操作会无限循环下去或者使得 x 超出 [1,n] 。对于 i,a2,a3,...,an(1i<n) 。如果不断循环,输出 -1,否则输出结束时 y 的值。

第一次操作之后 x = 1 + i,y = i,并 y 在操作中不断累加,这启发我们 dp。用 fx,0 表示先进行加操作,从 x 开始,y 初始取 0,直到最后时 y 的值,如果操作不能结束,令这个值为无穷大;同理 fx,1 表示先进行减操作的对应值。每个状态模拟当前的操作就不难转移,转移到正在递归的值即说明出现循环,具体不再赘述。当处理出所有的 f 之后,i,a2,a3,...,an(1i<n) 的答案即是 fi+1,1+i

#include <bits/stdc++.h>

const int MAX_N = 2e5 + 100;
const long long INFLL = 0x3f3f3f3f3f3f3f3fLL;

long long f[MAX_N][2];
bool vis[MAX_N][2];
int a[MAX_N];

long long dfs(int n, int x, int d)
{
    if (x > n || x < 1) {
        return 0;
    }
    if (x == 1) {
        return INFLL;
    }
    if (f[x][d] != -1) {
        return f[x][d];
    }
    if (vis[x][d]) {
        return INFLL;
    }
    vis[x][d] = true;
    long long ans;
    if (d == 0) {
        ans = dfs(n, x + a[x], !d);
    }
    else {
        ans = dfs(n, x - a[x], !d);
    }
    if (ans != INFLL) {
        ans += a[x];
    }
    return f[x][d] = ans;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i) {
        scanf("%d", a + i);
    }
    memset(f, -1, sizeof(f));
    for (int i = 2; i <= n; ++i) {
        dfs(n, i, 0);
        dfs(n, i, 1);
    }
    for (int i = 1; i < n; ++i) {
        if (f[i + 1][1] == INFLL) {
            printf("-1\n");
        }
        else {
            printf("%I64d\n", f[i + 1][1] + i);
        }
    }
    return 0;
}

F

一排 n 只奶牛,有些朝前看,有些朝后看。Iahub 按照一定顺序给牛挤奶,每头牛只挤一次。当一头牛看到某头牛被挤了奶,则会受到一点心理伤害。问牛心理伤害总和最小是多少。

两头相互看到的牛无论谁先都会产生一点心理伤害(来啊!相互伤害啊!!!),否则可以恰当安排挤奶顺序,使得这两头牛不会因为彼此受到伤害。故答案就是相互注视的牛的对数。

#include <bits/stdc++.h>

const int MAX_N = 2e5 + 10;

int l1[MAX_N];
int r0[MAX_N];
int a[MAX_N];

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", a + i);
    }
    for (int i = 1; i <= n; ++i) {
        l1[i] = l1[i - 1] + (a[i] == 1);
    }
    for (int i = n; i >= 1; --i) {
        r0[i] = r0[i + 1] + (a[i] == 0);
    }
    long long ans = 0;
    for (int i = 1; i <= n; ++i) {
        if (a[i] == 0) {
            ans += l1[i - 1];
        }
        else {
            ans += r0[i + 1];
        }
    }
    std::cout << ans / 2 << std::endl;
    return 0;
}

posted by 张静之

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值