2018CCPC吉林赛区 题解

1001. The Fool
整除分块,签到

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10, mod = 1e9 + 7;
int main() {
    int T, n, kase = 0;
    cin>>T;
    while (T--) {
        cin>>n;
        ll ans = 0;
        for (int  l = 1, r; l <= n; l = r + 1) {
            r = n / (n / l);
            ans += 1ll * (r - l + 1) * (n / l);
        }
        printf("Case %d: ", ++kase);
        if (ans % 2)
            puts("odd");
        else
            puts("even");
    }
}

1002. The World
签到

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
string s, a, b, tp;
map<string, int> mp;
int gao(string tmp) {
    if (tmp.length() == 5)
        return (tmp[0] - '0') * 10 + tmp[1] - '0';
    return tmp[0] - '0';
}
int main() {
    int T, n, kase = 0;
    mp["Beijing"] = 8;
    mp["Washington"] = -5;
    mp["London"] = 0;
    mp["Moscow"] = 3;
    cin>>T;
    while (T--) {
        cin>>s>>tp>>a>>b;
        string res = "Today";
        int time = gao(s);
        if (time == 12)
            time = 0;
        if (tp == "PM")
            time += 12;
        time += mp[b] - mp[a];
        if (time < 0) {
            res = "Yesterday";
            time += 24;
            if (time >= 12) {
                tp = "PM";
                time -= 12;
            }
            else {
                tp = "AM";
            }
        }
        else if (time >= 24) {
            res = "Tomorrow";
            time -= 24;
            if (time < 12)
                tp = "AM";
            else
                tp = "PM";
        }
        else {
            if (time >= 12)
                tp = "PM", time -= 12;
            else
                tp = "AM";
        }
        if (!time)
            time = 12;
        string s2 = "";
        if (time >= 10)
            s2 += (time / 10 + '0');
        s2 += (time % 10 + '0');

        int pos = 2;
        if (s.length() == 4)
            pos--;
        for (pos; pos < s.length(); pos++)
            s2 += s[pos];
        printf("Case %d: ", ++kase);
        cout<<res<<" "<<s2<<" "<<tp<<endl;

    }
}

1003. Justice

解法:首先,对于大于等于100000的ki值,我们不需要管,如果这些数总和大于等于1,那么肯定有解,我们先判断这些数总和是否大于等于1,我们用数组vis统计每个数出现次数,然后从大到小枚举x,vis[x] += vis[x + 1] / 2,如果到了最后vis[0]不为0,那么满足条件,第二步我们来组合,我们取出所有值小于等于20的ki,把他们全部变成2^(20 - ki),然后从到达小枚举这些数,直到这些数相加等于2^(19)为止,然后把这些数标记为1,其他全部标为0即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn], p[21], vis[maxn], vis2[maxn], ans[maxn];
struct node {
    int v, id;
    bool operator<(const node &t) const {
        return v < t.v;
    }
} b[maxn];
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    p[0] = 1;
    for (int i = 1; i <= 20; i++)
        p[i] = p[i - 1] * 2;
    while (T--) {
        int n, mx = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            ans[i] = 0;
            if (a[i] >= 100000)
                a[i] = 0;
            vis[a[i]]++;
            mx = max(mx, a[i]);
            b[i] = node{a[i], i};
        }
        vis[0] = 0;
        for (int i = 100000; i; i--)
        if (vis[i])
            vis[i - 1] += vis[i] / 2;
        printf("Case %d: ", ++kase);
        if (!vis[0]) {
            for (int i = 1; i <= mx; i++)
                vis[i] = 0;
            puts("NO");
            continue;
        }
        puts("YES");
        ll res = 0;
        sort(b + 1, b + 1 + n);
        for (int i = 1; i <= n; i++)
        if (b[i].v <= 20) {
            int tmp = p[20 - b[i].v];
            if (res + tmp <= p[19])
                ans[b[i].id] = 1, res += tmp;
        }
        for (int i = 1; i <= n; i++)
            if (a[i] >= 20)
                printf("0");
            else
                printf("%d", ans[i]);
        puts("");
        for (int i = 1; i <= mx; i++)
            vis[i] = 0;
    }
}

1004. The Moon

解法:设d[i][j]为 q 增加了 i 次 2%,j 次 1.5%,也就是 q = 0.02 + 0.02 * i + 0.015 * j,且仍然失败的概率,我们可以下一次就操作成功,那么ans += d[i][j] * p * q * (i + j + 1),下一次有可能直接打怪没打赢,d[i][j + 1] += d[i][j] * (1 - p),下一次也可能打赢怪但是没抽中奖,d[i + 1][j] += d[i][j] * p * (1 - q),如果当前的 q > 1,那么不用接续转移了,直接算贡献,ans += d[i][j] * (i + j) + d[i][j] / p。
#include<bits/stdc++.h>
#define db double
#define ll long long
using namespace std;
db d[52][70];
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int x;
        cin>>x;
        db p = 0.01 * x, ans = 0.0;
        for (int i = 0; i <= 50; i++)
            for (int j = 0; j <= 70; j++)
                d[i][j] = 0.0;
        d[0][0] = 1.0;
        for (int i = 0; i <= 49; i++) {
            for (int j = 0; j <= 70; j++) {

                db p1 = 0.02 + 0.02 * i + 0.015 * j;
                if (p1 > 1.0) {
                    ans += d[i][j] * (i + j) + d[i][j] / p;
                    break;
                }
                ans += d[i][j] * (i + j + 1) * p * p1;
                d[i + 1][j] += d[i][j] * p * (1.0 - p1);
                d[i][j + 1] += d[i][j] * (1.0 - p);
            }
        }
        printf("Case %d: %.10f\n", ++kase, ans);
    }
}

1006. The Hermit

题意:对于每个点 i, 它能覆盖范围是 [i - ri + 1, i + ri - 1],且对于任意相邻点i,i + 1,i + 1 的覆盖的左边界一定大于等于 i 覆盖的左边界,对于每个点 i,它的权值定义为合法k的数量,定义k合法:k < i,且 k 在 i 的覆盖范围内,且存在一个点 j,k < j < i,且 dist(k, j) >= dist(j, i),且 i 和 k 都在 j 的覆盖范围内。要求每个点的权值异或和
解法:题意很绕,题目贼水,对于 ri < 3 的点,显然权值为0,对于 ri >= 3 的点,我们由题意得,ri - 1 覆盖的左边界一定 <= ri 覆盖的左边界,也就是说我设置 j = i - 1,其他 i 左边能覆盖的点都可以作为合法的 k 点,也就是说, i 的权值为 ri - 2。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e6 + 10, N = 1e6;
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n, x, ans = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x);
            if (x >= 3)
                ans ^= (x - 2);
        }
        printf("Case %d: %d\n", ++kase, ans);
    }
}

1008. Lovers

解法:我们把每次操作分开处理,用tagR[o]标记往右边加的数字,R[o]为该区间右边加数字后的总和,len[o] 为该区间加了多少次,我们来下传一下标记,对于 fa 节点的信息要下传到 o 节点(假设 o 节点长度为n),首先R[o] *= 10^len[fa],然后R[o] += ragR[fa] * n,然后tagR[o] = tagR[o] * 10^len[fa],ok,右边加数很简单,现在来用tagL[o] 和 L[o] 来搞定左边加数,tagL[o]表示的数字是tagR[o]顺序是反着的,L[o]为该区间所有数字左边的一半乘以10^len/2的总和,设sum[o] 为区间 " 总和 ",初始值sum[o]为区间长度,每次更新了一次区间,那么该区间的sum[o] *= 100,下传标记:L[o] = L[o] * 10 ^len[fa] (因为右边加了这么多数) + tagL[fa] * sum[o] * 10^len[fa],tagL[o] = tagL[o] + tagL[fa] * 10^len[o]),最后更新len[o] += len[fa],sum[o] *= 10^(len[fa] * 2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10, mod = 1e9 + 7;
ll sum[maxn * 4], L[maxn * 4], R[maxn * 4], tagL[maxn * 4], tagR[maxn * 4], p[maxn];
int len[maxn * 4];
char s[20];
#define ls o * 2
#define rs o * 2 + 1
void gao(int o, int fa, int n) {
    R[o] = (R[o] * p[len[fa]]  + tagR[fa] * n) % mod;
    tagR[o] = (tagR[o] * p[len[fa]] + tagR[fa]) % mod;
    len[o] += len[fa];
    L[o] = (L[o] * p[len[fa]] % mod + tagL[fa] * sum[o] % mod * p[len[fa]] % mod) % mod;
    tagL[o] = (tagL[o] + tagL[fa] * p[len[o] - len[fa]]) % mod;
    sum[o] = sum[o] * p[len[fa] * 2] % mod;
}
void pushdown(int o, int l, int m, int r) {
    if (len[o]) {
        gao(ls, o, m - l + 1);
        gao(rs, o, r - m);
        tagL[o] = tagR[o] = len[o] = 0;
    }
}
void pushup(int o) {
    L[o] = (L[ls] + L[rs]) % mod;
    R[o] = (R[ls] + R[rs]) % mod;
    sum[o] = (sum[ls] + sum[rs]) % mod;
}
void build(int o, int l, int r) {
    L[o] = R[o] = tagL[o] = tagR[o] = len[o] = 0;
    if (l == r) {
        sum[o] = 1;
        return;
    }
    int m = (l + r) / 2;
    build(ls, l, m);
    build(rs, m + 1, r);
    pushup(o);
}
void up(int o, int l, int r, int ql, int qr, int x) {
    if (l >= ql && r <= qr) {
        tagR[o] = (tagR[o] * 10 + x) % mod;
        len[o]++;
        R[o] = (R[o] * 10 + 1ll * x * (r - l + 1)) % mod;
        L[o] = (L[o] * 10 + sum[o] * x * 10) % mod;
        tagL[o] = (tagL[o] + p[len[o] - 1] * x) % mod;
        sum[o] = sum[o] * 100 % mod;
        return;
    }
    int m = (l + r) / 2;
    pushdown(o, l, m, r);
    if (ql <= m)
        up(ls, l, m, ql, qr, x);
    if (qr > m)
        up(rs, m + 1, r, ql, qr, x);
    pushup(o);
}
ll qu(int o, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr)
        return (L[o] + R[o]) % mod;
    int m = (l + r) / 2;
    pushdown(o, l, m, r);
    ll res = 0;
    if (ql <= m)
        res += qu(ls, l, m, ql, qr);
    if (qr > m)
        res += qu(rs, m + 1, r, ql, qr);
    return res % mod;
}
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    p[0] = 1;
    for (int i = 1; i <= 200000; i++)
        p[i] = p[i - 1] * 10 % mod;
    while (T--) {
        int n, m, l, r, x;
        printf("Case %d:\n", ++kase);
        scanf("%d%d", &n, &m);
        build(1, 1, n);
        while (m--) {
            scanf("%s%d%d", s, &l, &r);
            if (s[0] == 'w') {
                scanf("%d", &x);
                up(1, 1, n, l, r, x);
            }
            else
                printf("%lld\n", qu(1, 1, n, l, r));
        }
    }
}

1009. Strength

题意:你有 m 个卡片要么处于攻击状态要么处于防御状态,我有 n 个卡片,每个卡片只能用一次,我如果选择一张卡片攻击你的攻击状态卡片,如果我的卡片攻击力>=你的卡片攻击力,那么你的那张被攻击的卡片就会作废,并且你会损失攻击力之差的血量,如果我选择一张卡片攻击你防御状态的卡片,如果我的卡片攻击力>=你的卡片攻击力,那么你的卡片作废,但是你不会损失血量,如果你没有卡片剩余,你损失的血量将是我剩余的卡片的攻击力总和。求你最大损失的血量。
解法:我们只要两个策略,第一个:我的所有卡片进攻你的攻击状态卡片,第二种:我先全部进攻你的防御状态卡片,如果我还有卡片剩余,那么再全部进攻你的攻击状态卡片,如果还有剩余,就全部进攻你。进攻防御状态的卡片的贪心策略过于简单,我们讲讲怎么进攻攻击状态的卡片,我门首先二分或者双指针求一下我最多能打赢对方几个卡片,然后我和对方的卡片对排序,假设我最多能打赢 k 个卡片,我门从 1 开始枚举 i,我最大的 i 张卡片的和 - 对方最小的 i 张卡片的和就是对方扣的血量,我么取一个最大值即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn], b[maxn], c[maxn], d[maxn], vis[maxn], n1, n2, n3;
ll sum1[maxn], sum2[maxn];
int ok(int k) {
    int p = n1 - k;
    for (int i = 1; i <= k; i++)
        if (a[i + p] < b[i])
            return 0;
    return 1;
}
ll gao(int tp) {
    if (!n2) {
        if (!tp)
            return 0;
        return sum1[n1];
    }
    if (a[n1] <= b[1])
        return 0;
    int l = 1, r = min(n1, n2);
    while (l < r) {
        int m = (l + r) / 2;
        if (ok(m))
            l = m + 1;
        else
            r = m;
    }
    if (!ok(l))
        l--;
    ll ans = 0;
    if (tp && l == n2)
        ans = sum1[n1] - sum2[n2];
    for (int i = 1; i <= l; i++)
        ans = max(ans, sum1[n1] - sum1[n1 - i] - sum2[i]);
    return ans;
}
int gao2() {
    if (!n3 || n1 == n3)
        return 1;
    int p = 1, q = 1;
    while (p <= n1 && q <= n3)
    if (a[p] >= c[q]) {
        vis[p] = 1;
        p++, q++;
    }
    else
        p++;
    if (q <= n3)
        return 0;
    int tmp = n1;
    n1 = 0;
    for (int i = 1; i <= tmp; i++)
        if (!vis[i])
            a[++n1] = a[i], sum1[n1] = sum1[n1 - 1] + a[n1];
    return 1;
}
int main() {
    int T, kase = 0;
    scanf("%d", &T);
    while (T--) {
        int n, m, x;
        scanf("%d%d", &n, &m);
        n1 = n2 = n3 = 0;
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[++n1]), vis[i] = 0;
        for (int i = 1; i <= m; i++)
            scanf("%d", &d[i]);
        for (int i = 1; i <= m; i++) {
            scanf("%d", &x);
            if (x)
                c[++n3] = d[i];
            else
                b[++n2] = d[i];
        }
        sort(a + 1, a + 1 + n1);
        sort(b + 1, b + 1 + n2);
        sort(c + 1, c + 1 + n3);
        for (int i = 1; i <= n1; i++)
            sum1[i] = sum1[i - 1] + a[i];
        for (int i = 1; i <= n2; i++)
            sum2[i] = sum2[i - 1] + b[i];
        ll ans = gao(0);

        if (gao2())
            ans = max(ans, gao(1));
        printf("Case %d: %lld\n", ++kase, ans);

    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值