ATCoder Regular Contest 172 A~E

A.Chocolate(贪心)

题意:

问题陈述

AtCoderAtCoderAtCoder女士决定在情人节向NNN位朋友分发巧克力。她想送给第iii个朋友(1≤i≤N)(1 \leq i \leq N)(1iN)一块2Ai×2Ai2^{A_i}\times 2^{A_i}2Ai×2Ai大小的正方形巧克力。

她购买了一块大小为H×WH\times WH×W的长方形巧克力。它被分割成HHH行和WWW列的网格,每个单元格都是一个1×11\times 11×1的正方形。

请判断是否可以将巧克力沿线分割成若干块,以满足她的分配方法。

分析:

对于从大小为H×WH\times WH×W的长方形巧克力上切出小块的巧克力,显然从角落切会比放在中间或者其他地方带来更多可以分配的块。从最大的开始切,然后将剩下的分成两个长方形放回去用,两种分块情况的影响是相同的,因为能切出的最大正方形由切出两块较小的长方形决定。(如果从最小的开始切可能还要判断一下大小再选择情况)

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 1010;

int h, w, n, tmp = 1;

map<int, int> two;
int cnt[N];
vector<pair<int, int>> v;

void solve() {
    for (int i = 0; i <= 25; i++) {
        two[i] = tmp;
        tmp <<= 1;
    }
    cin >> h >> w >> n;
    for (int i = 1; i <= n; i++) {
        cin >> tmp;
        cnt[tmp]++;
    }

    v.push_back({h, w});
    for (int i = 25; i >= 0; i--) {
        while (cnt[i]) {
            bool flag = false;
            for (int j = 0; j <= v.size() - 1; j++) {
                if (v[j].first >= two[i] and v[j].second >= two[i]) {
                    flag = true;
                    cnt[i]--;
                    int h = v[j].first, w = v[j].second;
                    v.push_back({h - two[i], two[i]});
                    v.push_back({h, w - two[i]});
                    v.erase(v.begin() + j);
                    break;
                }
            }
            if (!flag) {
                cout << "No" << endl;
                return;
            }
        }
    }
    cout << "Yes" << endl;
}

int main() {
    solve();
    return 0;
}

B.AtCoder Language(数学)

题意:

问题陈述

AtCoderAtCoderAtCoder语言有LLL个不同的字符。有多少个由AtCoderAtCoderAtCoder语言中的字符组成的长度为NNN的字符串sss满足以下条件?答案对998244353998244353998244353取模。

  • 字符串sss的所有KKK个字符其子序列都是不同的。更确切地说,从字符串sss中提取KKK个字符,然后在不改变顺序的情况下将它们连接起来,得到KKK个字符的字符串的方法有NCK_N\mathrm{C}_KNCK种,而所有这些方法一定会生成不同的字符串。

NCK_N\mathrm{C}_KNCK指的是从NNN项中选择KKK的方法的总数。更确切地说,NCK_N\mathrm{C}_KNCKN!N!N!除以K!×(N−K)!K! \times (N-K)!K!×(NK)!的值。

分析:

首先容易发现对于同一个字母,两次相邻的位置中间至少有n−kn-knk个字符。
现在有两种思路:对于每一个位置的字符,考虑它下一个的位置或者考虑转移到它有多少种方案。

容易发现,对于第一个字符,它有LLL种选法;而对于第二个,它有L−1L-1L1种选法(因为不能选第一个)以此类推,发现对于第iii个字符,它不能选的只是在它前面n−kn-knk个中出现的,而这显然是不会重复的,所以第iii个字符的选法是L−min(i−1,n−k)L-min(i-1,n-k)Lmin(i1,nk)

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 998244353;

int main() {
    int n, k, l, ans = 1;
    cin >> n >> k >> l;
    if (l < n - k) {
        cout << "0" << endl;
    } else {
        for (int i = 1; i <= n; i++) {
            if (i <= n - k)
                ans = 1ll * ans * (l - i + 1) % mod;
            else
                ans = 1ll * ans * (l - n + k) % mod;
        }
        cout << ans % mod << endl;
    }
    return 0;
}

C.Election(枚举)

题意:

问题陈述

今年的AtCoderAtCoderAtCoder市长选举有两位候选人AAABBBNNN位选民已经投了票。选民的编号从111NNN,选民iii (1≤i≤N)(1\leq i\leq N)(1iN)投票给候选人cic_ici(1≤i≤N)(1\leq i\leq N)(1iN)

现在开始计票。在计算每张选票时,将宣布以下三种结果之一的临时结果:

  • 结果AAA:候选人AAA得票较多。
  • 结果BBB:候选人BBB得票较多。
  • 结果CCC:候选人AAABBB的得票数相同。

计票顺序有一条规则:除111号选民外,其他选民的投票必须按照选民编号由大到小的顺序计算(111号选民的投票必须按照选民编号由小到大的顺序计算)。(投票人111的选票可以随时计算)。

可以宣布多少种不同顺序的临时结果?

临时结果序列:假设sis_isi是第iii个选票(1≤i≤N)(1\leq i\leq N)(1iN)计票时报告的结果(“A”、“B"或"C”)。临时结果序列指的是字符串s1、s2…sNs_1、s_2\dots s_Ns1s2sN

分析:

假设现在111号放在位置iii,且iii及之前的结果都计算完毕。如果111号挪到位置i+1i+1i+1,那么iii前面(不包括iii)的结果不受影响,i+1i+1i+1及以后的结果也不受影响。所以可以倒着模拟111号放在每处的情况。

定义两个变量sum1sum1sum1sum2sum2sum2,分别代表目前为止AAABBB的票数。记录一个字符数组cic_ici,代表前iii人投票的临时结果(要从第二个人开始算,可以整体向前移一位,即iii111n−1n−1n1)。输入时处理出前111n−1n−1n1人的sum1sum1sum1sum2sum2sum2(即前222nnn人)。倒推的时候到每一位调整当前的sum1sum1sum1sum2sum2sum2,代表第iii位时,选民111插在了第i−1i−1i1iii人之间(第iiii+1i+1i+1人之间)。然后再计算当前到第iii位临时选票结果,如果和之前的cic_ici不一样,说明有新的序列,对答案加一。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 1e6 + 10;
int n, sum1, sum2, ans;
char now, a[N], c[N], t;

int main() {
    cin >> n >> t;
    if (n == 1) {
        cout << 1 << endl;
        return 0;
    }
    for (int i = 1; i < n; i++) {
        cin >> a[i];
        if (a[i] == 'A')
            sum1++;
        else
            sum2++;
        if (sum1 == sum2)
            c[i] = 'C';
        else if (sum1 > sum2)
            c[i] = 'A';
        else
            c[i] = 'B';
    }
    for (int i = n; i; i--) {
        if (a[i] == 'A')
            sum1--;
        else if (a[i] == 'B')
            sum2--;
        if (t == 'A')
            sum1++;
        else
            sum2++;
        if (sum1 == sum2)
            now = 'C';
        else if (sum1 > sum2)
            now = 'A';
        else
            now = 'B';
        if (now != c[i])
            ans++;
        if (t == 'A')
            sum1--;
        else
            sum2--;
    }
    cout << ans << endl;
    return 0;
}

D.Distance Ranking(构造)

题意:

问题陈述

在一个NNN维空间中放置NNN个点p1,p2,…,pNp_1,p_2,\dots,p_Np1,p2,,pN以满足以下条件:

条件1 点的坐标由00010810^8108之间的整数组成,包括00010810^8108

条件2 对于指定为输入的(A1,B1),(A2,B2),…,(AN(N−1)/2,BN(N−1)/2)(A_1,B_1),(A_2,B_2),\dots,(A_{N(N-1)/2},B_{N(N-1)/2})(A1,B1),(A2,B2),,(AN(N1)/2,BN(N1)/2)d(pA1,pB1)<d(pA2,pB2)<⋯<d(pAN(N−1)/2,pBN(N−1)/2)d(p_{A_1},p_{B_1})\lt d(p_{A_2},p_{B_2})\lt\dots \lt d(p_{A_{N(N-1)/2}},p_{B_{N(N-1)/2}})d(pA1,pB1)<d(pA2,pB2)<<d(pAN(N1)/2,pBN(N1)/2)必须成立。d(x,y)d(x,y)d(x,y)表示点xxxyyy之间的欧几里得距离。

题目保证在问题的约束条件下至少存在一个解。如果存在多个解,只需输出其中一个。

分析:

考虑先构造任意两点之间距离相等, 再细微调整使其满足条件。

易发现有一种很明显的构造方式:p1={1,0,0,0,…0}p_1=\{1,0,0,0,\dots 0\}p1={1,0,0,0,0} p2={0,1,0,0,…0}p_2=\{0,1,0,0,\dots 0\}p2={0,1,0,0,0} p3={0,0,1,0,…0}p_3=\{0,0,1,0,\dots 0\}p3={0,0,1,0,0} …\dots pn={0,0,0,0,…1}p_n=\{0,0,0,0, \dots 1\}pn={0,0,0,0,1}

尝试把它转化成距离不等的方式,只需要在原基础上面加上很小很小的数zzz就可以实现,由于这个zzz非常小,所以之后的运算中,它的平方项都可以被忽略不计。(类似于求极限)

现在,我们得到的点是:p1={1+z1,1,z1,2,…,z1,n}p_1=\{1+z_{1,1},z_{1,2},\dots,z_{1,n}\}p1={1+z1,1,z1,2,,z1,n} p2={z2,1,1+z2,2,…,z2,n}p_2=\{z_{2,1},1+z_{2,2},\dots,z_{2,n}\}p2={z2,1,1+z2,2,,z2,n}

那么我们再对d(x,y)d(x,y)d(x,y)表示,直接化简并去掉平方项就可以得到:

d(x,y)=2−2(zx,y+zy,x)d(x,y)=\sqrt{2-2(z_{x,y}+z_{y,x})}d(x,y)=22(zx,y+zy,x)
于是我们发现影响这个的只是后面的zx,yz_{x,y}zx,y

那么一种构造方式就非常明显了,我们直接让一个为000,另一个按越在后面输入的越小即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 25;

int n, a[N][N], m;

int main() {
    cin >> n;
    m = n * (n - 1) / 2;
    for (int i = 1; i <= n; i++)
        a[i][i] = 1e8;
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        a[x][y] = m - i + 1;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << a[i][j] << " ";
        }
        cout << " ";
    }
    return 0;
}

E.Last 9 Digits(数学)

题意:

判断是否存在一个不是222或者555倍数的正整数nnn,使得nnn^nnn除以10910^9109的余数是XXX。如果存在,求最小的nnn

分析:

暴力枚举后发现似乎当n<109n<10^9n<109时,每个余数是唯一的。猜想nnn对于∀k∈N+∀k∈N^+kN+10k10^k10k取模是否也存在当n<10kn<10^kn<10knnn既不是222555的倍数时每个余数是唯一的。这个猜想是正确的,证明过程可参考官方题解。

由于当n<10kn<10^kn<10knnn既不是222555的倍数时nnn∀k∈N+∀k∈N^+kN+10k10^k10k取模每个余数唯一,我们可以预处理出小于等于一定位数kkk的所有nnmod  10kn^n\mod 10^knnmod10k,再把它们扔进一个映射中。对于一个数xxx,先通过xmod  10kx\mod10^kxmod10k确定nnn的末kkk位,再枚举1,2,...,109−k−11,2,...,10^{9−k}−11,2,...,109k1,和确定的nnn的末kkk位拼在一起验证mod  109\mod10^9mod109是否为xxx即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 1e9;
LL q, x;
map<LL, LL> vis;

LL qpow(LL a, LL b) {
    LL res = 1;
    while (b) {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main() {
    for (LL i = 1; i <= 1000000; ++i) {
        if (i % 2 == 0 || i % 5 == 0)
            continue;
        LL now = qpow(i, i);
        vis[now % 1000000] = i;
    }
    cin >> q;
    while (q--) {
        cin >> x;
        LL what = vis[x % 1000000];
        for (LL i = 0; i <= 1000; ++i) {
            LL cnt = what + i * 1000000;
            if (qpow(cnt, cnt) == x) {
                cout << cnt << endl;
                break;
            }
        }
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值