07_05_暑期个人赛2
B. Sum of Two Numbers
时间:2024-07-06
原题:Codeforces Round 851 (Div. 2)B. Sum of Two Numbers
题意
一个数字 \(n\) ,将其分解为两个数字 \(x\) 和 \(y\) ,要求:
-
\(x+y=n\)
-
\(dig(x)-dig(y)=\pm1\)
\(dig(x)\) 表示 \(x\) 的所有位数之和
思路
对于每一位,都可以将其平均,每次遇到奇数就分配给 \(x\) ,下一次遇到再给 \(y\)
代码
void solve() {
cin >> n;
if (n % 2 == 0) {
cout << n / 2 << " " << n / 2 << endl;
return;
}
// a1,a2是最终答案
int a1=0, a2=0;
// i表示分解的位数
int i = 0;
// c表示分解n的第i位时如果出现奇数将较大的一个数给a1还是a2
int c = 0;
while (n) {
int d = pow(10, i);
int base = n % 10;
int base1 = base / 2, base2 = base / 2 + base % 2;
if (c % 2 == 1) {
a1 += base1 * d;
a2 += base2 * d;
if(base1!=base2)c++;
}
else {
a1 += base2 * d;
a2 += base1 * d;
if (base1 != base2)c++;
}
n /= 10;
i++;
}
cout << a1 << " " << a2 << endl;
}
C. New Year and Rating
时间:2024-07-06
原题:Good Bye 2016 C. New Year and Rating
题意
1900是一个分界线,线上规定为等级1(包括1900),线下规定为等级2
有 \(n\) 次比赛,每次给出比赛前的等级和本次比赛获得的分数,要求输出
思路
对于等级和得分,我们可以用一个 \(value\) 表示改变的可能性
即如果为等级1,并且是加分,\(value=1\) ,等级不会改变
并且可以知道此时的分数变化对上下限不产生影响
如果等级为1,但是是扣分,\(value=-1\),等级可能会影响
- 如果等级没掉,可以确定下限
- 如果等级掉了,可以确定上限
最后如果出现错误的等级变化或者上限小于下限,则Impossible
代码
#define IMP "Impossible"
#define INF "Infinity"
#define int long long
#define rep(i,j,k) for(i=(j);i<(k);i++)
const int N = 2e5+10;
int ii, jj, kk;
int n;
struct Node {
int c, d;
}node[N];
int sum[N];
void solve() {
cin >> n;
sum[0] = 0;
rep(ii, 1, n+1) {
cin >> node[ii].c >> node[ii].d;
sum[ii] = sum[ii - 1] + node[ii].c;
}
//上界下界,包含边界
int to = 1e9, bo = -1e9;
if (node[1].d == 1) bo = 1900;
else to = 1899;
rep(ii, 1, n) {
//在线上往下降
//在线下往上升
//线上1 上升1
int value = (node[ii].d == 1 ? 1 : -1) * (node[ii].c >= 0 ? 1 : -1);
if (node[ii].c == 0 && node[ii].d!=node[ii+1].d) {
cout << IMP << endl;
return;
}
//有判断价值
if (value < 0) {
//当前在1,但是掉分
if (node[ii].d == 1) {
//掉分没掉下1
if (node[ii + 1].d == 1) {
bo = max(bo, 1900 - sum[ii]);
}
//掉分掉到2
else {
to = min(to, 1899 - sum[ii]);
}
}
else {
//上大分
if (node[ii + 1].d == 1) {
bo = max(bo, 1900 - sum[ii]);
}
//上小分
else {
to = min(to, 1899 - sum[ii]);
}
}
}// 可以融合成一个
// value>0时
// 如果线上上分变成线下||线下掉分变成线上
else {
if (node[ii].d != node[ii + 1].d) {
cout << IMP << endl;
return;
}
}
}
if (bo > to) {
cout << IMP << endl;
}
else if (to != 1e9) {
cout << to + sum[n] << endl;
}
else {
cout << INF << endl;
}
}
D. Petya, Petya, Petr, and Palindromes
时间:2024-07-06
原题:Codeforces Round 861 (Div. 2)D. Petya, Petya, Petr, and Palindromes
思路来源:https://zhuanlan.zhihu.com/p/618268493
题意
给定序列 \(a\),长度为\(n\),要求对在 \(a\) 中的所有长度为 \(k\) 的序列进行处理,要替换几个数字能将其变成回文序列
\(k\) 为奇数
比如 \([1,2,3,3,1,4,5]\),\(k\) 为5,那么对于子序列之一\([1,2,3,3,1]\),需要变3为2,\([1,2,3,2,1]\),答案加1
思路
首先排除暴力,但是如果要计算所有不同的对数,必须要每个比较一次,则这个思路不行
所以选择计算所有的可能,然后再减去相同的对数。
1
在一开始对题目的尝试中可以获知当前位要与其他邻近的同奇偶性的位置进行比较
那么我们可以先按奇偶存储一个数的下标,当下次再遇到这个数的下标时根据k判断是否成立
比如对于 \(a=[1,2,1,2,1,2,1,2,1]\),\(n\) 为9,\(k\) 为5,从前遍历,先只看奇数
遍历到 \(a_3\) 时,在\(v[1]\)中存储了下标1,但是3与1号位本身不成对
遍历到 \(a_5\) 时,在\(v[1]\)中存储下标1,3,对于1和5成对,1和3也成对,那么答案需要减去2
遍历到 \(a_7\) 时,\(v[1]=[1,3,5]\),答案减去3
遍历到 \(a_9\) 时,\(v[1]=[1,3,5,7]\),9只与5成对,答案减1
2
对于基本的流程了解后,现在需要确定初始所有的答案,和成对的条件
我们引入对称中心的概念,记为c,可知 \(\frac{k+1}{2}<=c<=n-\frac{k+1}{2}+1\)
那么对于初始答案,有 \((n-\frac{k+1}{2}+1)-(\frac{k+1}{2})\) 个对称中心,每个对称中心对应有\(\frac{k-1}{2}\)对数字
相乘则为答案
对于成对的条件,记当前遍历到的位数为i,与之对应的成对下标为j,可知\(c=\frac{i+j}{2}\)
将c带入进上面的式子,就是\(k+1-i<=j<=2n-k+1-i\)
还有一个条件,\(i\) 和 \(j\) 的距离不能大于 \(k\) ,即\(i-j>k-1\),
综上, \(max(k-i+1,i-k+1)\le j \le 2n-k+1-i\)
最后用lower_bound和upper_bound快速获取答案
代码
const int N = 2e5 + 10;
int n, k;
vector<int> v[N][2];
void solve() {
cin >> n >> k;
// 给ans赋初值
int ans = (n - k + 1) * (k / 2);
int x = 0;
rep(ii, 1, n + 1) {
// 对于每个x 看有没有能与之配对且符合条件的
cin >> x;
auto posl = lower_bound(alli(v[x][ii & 1]), max(k - ii + 1, ii - k + 1));
auto posr = upper_bound(alli(v[x][ii & 1]), 2 * n - k - ii + 1);
//cout << "ii:" << ii << " posl:" << posl << " posr:" << posr << endl;
ans -= posr - posl;
v[x][ii & 1].push_back(ii);
}
cout << ans << endl;
}