GDCPC2015题解 by lby@SYSU_Spirit_Moon

本文详细介绍了GDCPC2015算法竞赛的多个问题,包括Article、Base64、Calculator等多个题目。针对每个问题,文章提供了题目大意、算法思路、时间复杂度分析及解决方案的代码,帮助读者理解并解决竞赛中的各种算法挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem A. Article

题目大意

DRD要在一个编辑器里打出N个字符并保存
对于某一时刻,有两种操作:

  • 保存当前的结果,需要x单位时间
  • 打出一个字符,需要1单位时间,且有p的概率回到上个保存点

问最优策略下,完成任务的用时期望是多少

算法思路

DP,f[i]表示打出前i个字符并保存所需用时的期望
转移方程为f[i] = min{f[j] + g[i-j] + x}
其中g[i]表示连续打出i个字符不出错的用时期望
可以得到递推式g[i] = (g[i-1] + 1) * (1 - p) + (g[i-1] + 1 + g[i]) * p
整理得g[i] = (g[i-1] + 1) / (1 - p)
不难发现,g的增长速率非常快,故转移时只需考察前面很少的状态

时间复杂度: O(kN)

代码

/**
 * Copyright (c) 2015 Authors. All rights reserved.
 * 
 * FileName: A.cpp
 * Author: Beiyu Li <sysulby@gmail.com>
 * Date: 2015-05-25
 */
#include <bits/stdc++.h>

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 100000 + 5;

int n, x;
double p, f[maxn];

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                scanf("%d%lf%d", &n, &p, &x);
                p = 1 - p;
                f[0] = 0;
                For(i,1,n) {
                        double g = 1 / p;
                        f[i] = f[i-1] + g + x;
                        for (int j = i - 2; j >= 0; --j) {
                                g = (g + 1) / p;
                                if (g > f[i]) break;
                                f[i] = min(f[i], f[j] + g + x);
                        }
                }
                printf("Case #%d: %.10f\n", ++cas, f[n]);
        }

        return 0;
}

Problem B. Base64

题目大意

输入一个整数k和一个字符串,输出该字符串使用Base64编码k次后的结果

算法思路

模拟,使用bitset可以很方便的实现字符与01串的相互转换

时间复杂度: O(KL)

代码

/**
 * Copyright (c) 2015 Authors. All rights reserved.
 * 
 * FileName: B.cpp
 * Author: Beiyu Li <sysulby@gmail.com>
 * Date: 2015-05-25
 */
#include <bits/stdc++.h>

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 100000 + 5;

int k;
char s[maxn];
char mp[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

void trans()
{
        string bit;
        int n = strlen(s);
        rep(i,n) bit += bitset<8>(s[i]).to_string();
        while (bit.length() % 6) bit += '0';
        n = 0;
        for (int i = 0; i < bit.length(); i += 6)
                s[n++] = mp[bitset<6>(bit.substr(i, 6)).to_ulong()];
        while (n % 4) s[n++] = '=';
        s[n] = 0;
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                scanf("%d%s", &k, s);
                while (k--) trans();
                printf("Case #%d: %s\n", ++cas, s);
        }

        return 0;
}

Problem C. Calculator

题目大意

给出一个长度为N,只包含加、乘、乘方的运算序列

  • 例如:*4, +2, ^3, +8, *6

要求支持两种操作:

  • 输入x,求依次运算后对29393取模的结果
    • 例如 x = 2: ((((24)+2)3)+8)6)=6048
  • 修改序列中某个位置的运算

算法思路

对29393分解质因数得29393 = 7 * 13 * 17 * 19

  • 对于修改,可以使用线段树维护对每个因子取模时的映射,表示某个值经过该区间上的运算所得的值
  • 对于询问,通过整个区间的映射得出对每个因子取模后的值,再通过中国剩余定理合并

具体的线段树维护方式,请参照下面的代码

时间复杂度: O(NlogN+MlogN)

代码

/**
 * Copyright (c) 2015 Authors. All rights reserved.
 * 
 * FileName: C.cpp
 * Author: Beiyu Li <sysulby@gmail.com>
 * Date: 2015-05-25
 */
#include <bits/stdc++.h>

using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)
#define For(i,s,t) for (int i = (s); i <= (t); ++i)
#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

typedef long long LL;
typedef pair<int, int> Pii;

const int inf = 0x3f3f3f3f;
const LL infLL = 0x3f3f3f3f3f3f3f3fLL;

const int maxn = 50000 + 5;
const int mod[] = {
  7, 13, 17, 19};

int n, m;
char p[maxn];
int v[maxn], r[4];
int f[1<<18][4][20];

int pow_mod(int a, int b, int m)
{
        int res = 1;
        for (; b; a = a * a % m, b >>= 1) if (b & 1) res = res * a % m;
        return res;
}

void gcd(LL a, LL b, LL &g, LL &x0, LL &y0)
{
        if (!b) g = a, x0 = 1, y0 = 0;
        else gcd(b, a % b, g, y0, x0), y0 -= x0 * (a / b);
}

int china(int r[], const int m[], int n)
{
        LL r0 = r[0], m0 = m[0];
        for (int i = 1; i < n; ++i) {
                LL r1 = r[i], m1 = m[i], g, x0, y0;
                gcd(m0, m1, g, x0, y0);
                if ((r1 - r0) % g) return -1;
                LL r2 = (r1 - r0) / g * x0 % m1, m2 = m0;
                m0 = m0 / g * m1;
                r0 = (r0 + r2 * m2 % m0) % m0;
        }
        return (r0 + m0) % m0;
}

void update(int o, int l, int r, int i)
{
        if (l == r) {
                rep(i,4) rep(j,mod[i]) {
                        if (p[l] == '+') f[o][i][j] = (j + v[l]) % mod[i];
                        if (p[l] == '*') f[o][i][j] = (j * v[l]) % mod[i];
                        if (p[l] == '^') f[o][i][j] = pow_mod(j, v[l], mod[i]);
                }
                return;
        }
        int mid = (l + r) >> 1, lc = o * 2, rc = o * 2 + 1;
        if (i == -1 || i <= mid) update(lc, l, mid, i);
        if (i == -1 || i > mid) update(rc, mid + 1, r, i);
        rep(i,4) rep(j,mod[i]) f[o][i][j] = f[rc][i][f[lc][i][j]];
}

int main()
{
        int T, cas = 0;
        scanf("%d", &T);

        while (T--) {
                scanf("%d%d\n", &n, &m);
                For(i,1,n) p[i] = getchar(), scanf("%d\n", &v[i]);
                update(1, 1, n, -1);
                printf("Case #%d:\n", ++cas);
                while (m--) {
                        int op, x, i;
                        scanf("%d", &op);
                        if (op == 1) {
                                scanf("%d", &x);
                                rep(i,4) r[i] = f[1][i][x%mod[i]];
                                printf("%d\n", china(r, mod, 4));
                        } else {
                                scanf("%d ", &i);
                                p[i] = getchar(), scanf("%d", &v[i]);
                                update(1, 1, n, i);
                        }
                }
        }

        return 0;
}

Problem D. Doom

题目大意

给出一个长度为N的数列,以及M个查询,初始答案为0
对于每个查询

  • 将数列在[l, r]区间上的和累加到答案中,输出答案对p = 9223372034707292160取模后的值
  • 将数列在[l, r]区间上的每个数变成自己的平方

算法思路

p=9223372034707292160=231×3×5×17×257×65536
单独考虑p的每个质因子q,都是 2k+1 形式的素数,而数列中的数又是 x2y 的形式
因为 ab mod p=ab mod φ(p) mod p
所以, x2y mod q=x2y mod φ(q) mod q=x2y

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值