A.Soccer(思维)
题意:
迪马喜欢看足球比赛。在这样一场比赛中,记分牌上的比分表示为xxx:yyy,其中xxx是第一队的进球数,yyy是第二队的进球数。在任何时候,只有一支球队可以进球,因此比分xxx:yyy可以变为(x+1)(x+1)(x+1):yyy,或者xxx:(y+1)(y+1)(y+1)。
在观看足球比赛时,迪马被一些非常重要的事情分散了注意力,过了一段时间,他又重新开始观看比赛。迪马记得他走神前的比分和他回来后的比分。鉴于这两个比分,他想知道以下问题。有没有可能在迪马没有看比赛的时候,两队的比分从来没有相同过?
可以肯定的是,在迪马记得的两个时间点上,两队的比分都不相等。不过,也有可能在他不在的时候,比分并没有发生变化。
请帮助迪马回答问题!
分析:
我们将之前的比分表示为[x1,y1][x_1,y_1][x1,y1],之后的比分表示为[x2,y2][x_2,y_2][x2,y2]。二者不相交则不可能出现相同的分数。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve() {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
if (x1 < y1) {
if (x2 >= y2) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
} else if (x1 > y1) {
if (x2 <= y2) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
} else {
cout << "YES" << endl;
}
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
B.Collatz Conjecture(思维、数学)
题意:
最近,一年级学生马克西姆学习了科拉兹猜想,但他在讲课时没有太注意,所以他认为猜想中提到了以下过程:
有一个变量xxx和一个常数yyy。下面的操作进行了kkk次:
- 将xxx增加111,然后
- 当数字xxx能被yyy整除时,再除以yyy。
请注意,这两个操作都是在一次操作中依次进行的。
例如,如果数字x=16x=16x=16、y=3y=3y=3和k=2k=2k=2,那么经过一次运算后,xxx就变成了171717,而经过另一次运算后,xxx就变成了222,因为加一后,x=18x=18x=18就能被333整除两次。
鉴于初始值为xxx、yyy和kkk,马克西姆想知道xxx的最终值是多少。
分析:
直接模拟会超时。考虑如果xxx等于111,可以直接跳出循环,用数学方法解决,因为这时xxx就一直会在[1,y][1,y][1,y]之间。
代码:
#include<bits/stdc++.h>
using namespace std;
void solve() {
int x, y, k;
cin >> x >> y >> k;
while (k != 0 && x != 1) {
int t = min(k, y - x % y);
x += t;
k -= t;
while (x % y == 0) {
x /= y;
}
}
k %= (y - 1);
x = x + k;
cout << x << endl;
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
C.Boring Day(贪心)
题意:
又是无聊的一天,埃戈尔觉得无聊,决定做点什么。但因为没有朋友,他就想出了一个游戏。
埃戈尔有一副nnn张扑克牌,最上面的iii张牌上写着数字aia_iai。埃戈想玩一定轮数的游戏,直到牌用完为止。在每一轮中,他都会从牌面顶端抽取一张非零数的牌,然后结束这一轮。如果在这一轮中收集到的纸牌上的数字之和在lll和rrr(包括首尾数字)之间,则这一轮获胜;否则,这一轮失败。
埃戈尔对纸牌的顺序了如指掌。请帮助埃戈尔确定他在这样的游戏中最多可以赢多少局。请注意,伊戈尔不需要连续赢得回合。
分析:
本题我们考虑贪心,使用双指针先一直往后加和(后面的指针往后走),当和不小于lll时,将前指针也往后走,假如现所在位置为lll。那么就将sum−alsum−a_lsum−al。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 1e6 + 5;
LL a[N];
void solve() {
int n, L, R;
cin >> n >> L >> R;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
LL cnt = 0;
LL sum = 0;
int l = 0;
for (int r = 1; r <= n; r++) {
sum += a[r];
while (l + 1 <= r && sum > R) {
sum -= a[++l];
}
if (sum >= L && sum <= R) {
cnt++;
sum = 0;
l = r;
}
}
cout << cnt << endl;
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
D.Beauty of the mountains(数学)
题意:
尼基塔喜欢山,终于决定去贝里安德山脉看看!这座山脉如此美丽,尼基塔决定用地图记录下来。地图是一个由nnn行和mmm列组成的表格,每个单元格都包含一个非负整数,代表山的高度。
他还注意到,山有两种类型:
- 有雪盖。
- 无雪帽。
尼基塔是一个非常务实的人。他希望有雪帽的山的高度总和等于没有雪帽的山的高度总和。他已经和贝里安德的市长波利卡普-波利卡波维奇达成了协议,允许他改造地貌。
尼基塔可以对大小为k×kk\times kk×k的子矩阵进行如下变换:他可以在该区域内的山峰高度上添加一个整数常数ccc,但山峰的类型保持不变。尼基塔可以为每次变换独立选择常数ccc。注意ccc可以是负数。
在进行变换之前,尼基塔会要求您找出是否有可能实现总和相等,或者是否不可能。即使山变成了峡谷,高度为负数,代价也无所谓。
如果地图上只有一种类型的山,那么另一种类型的山的高度之和将被视为零。
分析:
我们考虑到,如果要让这两部分分别相等(000部分的和为sum0sum_0sum0,111部分的和为sum1sum_1sum1),那么我们对于每一个k×kk×kk×k的矩阵mim_imi,假设当前矩阵被选且其中有numi,0num_{i,0}numi,0个000,和numi,1num_{i,1}numi,1个111,且当前答案系数为cic_ici。
我们令sum0sum_0sum0加上ck×numi,0c_k×num_{i,0}ck×numi,0,sum1sum_1sum1加上ck×numi,1c_k×num_{i,1}ck×numi,1,那么sum0sum_0sum0会加上∑ci×numi,0(i∈S)\sum c_i×num_{i,0}(i∈S)∑ci×numi,0(i∈S),sum1sum_1sum1会加上∑ci×numi,1(i∈S)\sum c_i×num_{i,1}(i∈S)∑ci×numi,1(i∈S)。这两部分加上原来的部分就是我们要求的相等的和,所以设原来两部分的差为mnmnmn,这样则有
∑ci×∣numi,0−numi,1∣=mn(i∈S)\sum c_i×∣num_{i,0}−num_{i,1}∣=mn(i∈S)∑ci×∣numi,0−numi,1∣=mn(i∈S)。
而绝对值内的部分可以通过二维前缀和预处理,ccc为未知数,所以这是不定方程的形式,我们利用裴蜀定理,求出所有∣numi,0−numi,1∣∣num_{i,0}−num_{i,1}∣∣numi,0−numi,1∣的gcdgcdgcd(如果其中存在000则跳过),如果所有这些数都是000且mn=0mn=0mn=0,或者这个gcdgcdgcd整除mnmnmn则有方案,否则无方案。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 5e2 + 2;
LL gcd(int a, int b) {
return b > 0 ? gcd(b, a % b) : a;
}
LL n, m, k, a[N][N], c[N][N];
string s[N];
LL cal1(int x1, int y1, int x2, int y2) {
return c[x2][y2] - c[x1 - 1][y2] - c[x2][y1 - 1] + c[x1 - 1][y1 - 1];
}
LL cal2(int x1, int y1, int x2, int y2) {
return a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1];
}
void solve() {
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i = 1; i <= n; i++) {
cin >> s[i];
s[i] = " " + s[i];
for (int j = 1; j <= m; j++) {
if (s[i][j] == '0') {
c[i][j] = -1;
a[i][j] *= -1;
} else {
c[i][j] = 1;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1];
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
LL g = 0;
for (int i = 1; i + k - 1 <= n; i++) {
for (int j = 1; j + k - 1 <= m; j++) {
LL sum = cal1(i, j, i + k - 1, j + k - 1);
sum = abs(sum);
g = gcd(g, sum);
}
}
LL sum = cal2(1, 1, n, m);
if ((g == 0 && sum == 0) || (g != 0 && sum % g == 0)) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
E.Number of k-good subarrays(思维)
题意:
让bit(x)bit(x)bit(x)表示非负整数xxx的二进制表示中的111的个数。
如果一个数组的子数组只由二进制表示中的111数不超过kkk的数组成,那么这个数组被称为kkk(好)数组,对于数组aaa的子数组(l,r)(l,r)(l,r),当满足条件bit(ai)≤kbit(a_i)≤kbit(ai)≤k对于所有l≤i≤rl≤i≤rl≤i≤r的iii时,则该子数组是kkk好的。
给你一个长度为nnn的数组aaa,由从000开始的连续的非负整数组成,即0≤i≤n−10\le i\le n-10≤i≤n−1的ai=ia_{i}=iai=i。你需要计算这个数组中kkk好的子数组的数量。
由于答案可能非常大,所以请输出对109+710^{9}+7109+7取模的结果。
分析:
设sol(n,k)sol(n,k)sol(n,k)为答案。(注意,是[0,n−1][0,n-1][0,n−1]的)那么sol(n,k)sol(n,k)sol(n,k)可以通过sol(mx,k)sol(mx,k)sol(mx,k)和sol(n−mx,k−1)sol(n-mx,k-1)sol(n−mx,k−1)算出来,其中mxmxmx是小于nnn的最大222次幂。(大于等于mxmxmx的就会有一位固然是111,就会是k−1k-1k−1。)设mx=2cmx=2^cmx=2c。
除了内部的,还有一个端点在左边一个端点在右边的。官方题解是维护了三个值,较为复杂。
我们发现,如果c>kc\gt kc>k,那么左边最后一个就会有大于kkk个111,没有贡献,所以必须c≤kc\le kc≤k。这个时候左边所有的都满足,只需要计算右边的。右边的到了2k−12^k-12k−1也不行,因为这样也超过了(当然要和长度取最小值)。所以我们算出了右边的贡献s=min(2c−1,n−2c)s=min(2^c-1,n-2^c)s=min(2c−1,n−2c)。那么,答案就要多加上s×2cs\times 2^cs×2c。直接记忆化搜索即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL MOD = 1e9 + 7;
map<pair<LL, LL>, LL> mp;
LL sol(LL n, LL k) {
if (k == 0 || n == 1) {
return 1;
}
if (mp.count({n, k})) {
return mp[{n, k}];
}
LL c = 0;
while ((1ll << c + 1) < n) {
c++;
}
LL l = sol(1ll << c, k);
LL r = sol(n - (1ll << c), k - 1);
LL ans = l + r;
if (c <= k) {
LL s = min((1ll << k) - 1, n - (1ll << c));
ans += s % MOD * ((1ll << c) % MOD) % MOD;
}
mp[{n, k}] = ans % MOD;
return ans % MOD;
}
void solve() {
LL n, k;
cin >> n >> k;
cout << sol(n, k) << endl;
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

396



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



