A.Chocolate(贪心)
题意:
问题陈述
AtCoderAtCoderAtCoder女士决定在情人节向NNN位朋友分发巧克力。她想送给第iii个朋友(1≤i≤N)(1 \leq i \leq N)(1≤i≤N)一块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}_KNCK是N!N!N!除以K!×(N−K)!K! \times (N-K)!K!×(N−K)!的值。
分析:
首先容易发现对于同一个字母,两次相邻的位置中间至少有n−kn-kn−k个字符。
现在有两种思路:对于每一个位置的字符,考虑它下一个的位置或者考虑转移到它有多少种方案。
容易发现,对于第一个字符,它有LLL种选法;而对于第二个,它有L−1L-1L−1种选法(因为不能选第一个)以此类推,发现对于第iii个字符,它不能选的只是在它前面n−kn-kn−k个中出现的,而这显然是不会重复的,所以第iii个字符的选法是L−min(i−1,n−k)L-min(i-1,n-k)L−min(i−1,n−k)。
代码:
#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市长选举有两位候选人AAA和BBB,NNN位选民已经投了票。选民的编号从111到NNN,选民iii (1≤i≤N)(1\leq i\leq N)(1≤i≤N)投票给候选人cic_ici。(1≤i≤N)(1\leq i\leq N)(1≤i≤N)
现在开始计票。在计算每张选票时,将宣布以下三种结果之一的临时结果:
- 结果AAA:候选人AAA得票较多。
- 结果BBB:候选人BBB得票较多。
- 结果CCC:候选人AAA和BBB的得票数相同。
计票顺序有一条规则:除111号选民外,其他选民的投票必须按照选民编号由大到小的顺序计算(111号选民的投票必须按照选民编号由小到大的顺序计算)。(投票人111的选票可以随时计算)。
可以宣布多少种不同顺序的临时结果?
临时结果序列:假设sis_isi是第iii个选票(1≤i≤N)(1\leq i\leq N)(1≤i≤N)计票时报告的结果(“A”、“B"或"C”)。临时结果序列指的是字符串s1、s2…sNs_1、s_2\dots s_Ns1、s2…sN。
分析:
假设现在111号放在位置iii,且iii及之前的结果都计算完毕。如果111号挪到位置i+1i+1i+1,那么iii前面(不包括iii)的结果不受影响,i+1i+1i+1及以后的结果也不受影响。所以可以倒着模拟111号放在每处的情况。
定义两个变量sum1sum1sum1和sum2sum2sum2,分别代表目前为止AAA和BBB的票数。记录一个字符数组cic_ici,代表前iii人投票的临时结果(要从第二个人开始算,可以整体向前移一位,即iii为111到n−1n−1n−1)。输入时处理出前111到n−1n−1n−1人的sum1sum1sum1和sum2sum2sum2(即前222到nnn人)。倒推的时候到每一位调整当前的sum1sum1sum1和sum2sum2sum2,代表第iii位时,选民111插在了第i−1i−1i−1和iii人之间(第iii和 i+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 点的坐标由000到10810^8108之间的整数组成,包括000和10810^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(N−1)/2,BN(N−1)/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(N−1)/2,pBN(N−1)/2)必须成立。d(x,y)d(x,y)d(x,y)表示点xxx和yyy之间的欧几里得距离。
题目保证在问题的约束条件下至少存在一个解。如果存在多个解,只需输出其中一个。
分析:
考虑先构造任意两点之间距离相等, 再细微调整使其满足条件。
易发现有一种很明显的构造方式: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)=2−2(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^+∀k∈N+,10k10^k10k取模是否也存在当n<10kn<10^kn<10k且nnn既不是222或555的倍数时每个余数是唯一的。这个猜想是正确的,证明过程可参考官方题解。
由于当n<10kn<10^kn<10k且nnn既不是222或555的倍数时nnn对∀k∈N+∀k∈N^+∀k∈N+,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,...,109−k−1,和确定的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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1524

被折叠的 条评论
为什么被折叠?



