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;
}