arc123 C、D 题解(haven't finish.
link
感悟:其实,一道题不妨从样例入手。
C
考场上 dfs “水过去” 的(感觉可以剪掉很多枝,常数 \(\frac{1}{500}\)),考后发现是正解(?
至于证明,现在不想理解,以后来填吧。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;
int T, ans, maxx, i, g;
LL n, t, f, no;
bool dfs(LL t, int nyhsb, int limit_, int las) {
// printf("|%lld %d %d %d|\n", t, nyhsb, limit_, las);
if(t == 0) return (!nyhsb);
int g = t % 10, flag = 0; g -= nyhsb;
if(g == -1) g = 9, flag = 1;
if((int)ceil(g / 3.0) <= las) {
if(las == 0x3f3f3f3f && dfs(t / 10, flag, 1, min(las, g))) return 1;
if(las != 0x3f3f3f3f && dfs(t / 10, flag, 1, min(las, g))) return 1;
}
// if(g + 10 <= i * 3) {
// printf("rnm:%d %d %d\n", t, (int)ceil((g + 10) / 3.0), g);
if((int)ceil((g + 10) / 3.0) > las) return 0;
if(las == 0x3f3f3f3f && dfs(t / 10, 1, 1, min(las, g + 10))) return 1;
if(las != 0x3f3f3f3f && dfs(t / 10, 1, 1, min(las, g + 10))) return 1;
// }
return 0;
}
int main() {
scanf("%d", &T); ans = 0;
while(T --) {
scanf("%lld", &n); ans = 0;
for(i = 1; ; i ++) {
t = n;
// if(i <= 3) {
// no = 0; t = n; maxx = 0; f = 0;
// while(t) {
// // printf("|%d %lld %d|\n", i, t, f);
// g = t % 10; t /= 10;
// if(g > i * 3) { no = 1; break; }
// else if((int)ceil(g / 3.0) == i) f = 1;
// else if(!f) { no = 1; break; }
// }
// if(no) continue; ans = i; break;
// }
if(dfs(t, 0, 0, i)) { ans = i; break; }
// if(i > 100) { printf("NYH yydsb!"); break; }
}
printf("%d\n", ans);
}
return 0;
}
/*
2
40
9025745
*/
D
因为考场上将 \(C_i\) 替换成了 \(A_i-B_i\),现在就按照这个讲吧,其实不换直接做也可以。。
由题意得 \(B_i \leqslant B_{i+1}\),\(A_i-B_i \geqslant A_{i+1}-B_{i+1}\),\(B_{i+1} \geqslant B_i+max(0,A_{i+1}-A_i)\), \(ans=\sum |B_i|+|A_i-B_i|\)。不妨找找 \(B\) 序列的关系,考虑两种情况。
1.\(A_{i+1}\geqslant A_i\),贪心地来想,我们肯定要使 \(B_{i+1}\) 和 \(A_{i+1}-B_{i+1}\) 都为正数。即 \(B_{i_+1}\) 为正数,并且在可能的范围内最小,则 \(B_{i+1}=B_i+A_{i+1}-A_i\)。
2.\(A_{i+1}<A_i\),\(B_{i+1}=B_i\)。
然后就结了。\(ans=\sum |d+B_1|+|c-B_1|\)(\(c、d\) 为常数,可用上面的柿子计算)。把 \(|B_1|\) 看成 \(|0-B_1|\),然后这道题就是求一个中位数。暴力跑是 \(\mathcal {O(nlog_2(n))}\)。
这里有一个 trick,用类似快排的东西找第 \(k\) 大数是 \(\mathcal {O(n)}\) 的。
暴力跑:
```cpp
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;
const int MAXN = 4e5 + 5;
int n, a[MAXN];
LL ans, c[MAXN];
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) {
if(a[i] >= a[i - 1]) c[i] = c[i - 1] + a[i] - a[i - 1];
else c[i] = c[i - 1];
}
for(int i = n + 1; i <= 2 * n; i ++) c[i] = a[i - n] - c[i - n];
for(int i = 1; i <= n; i ++) c[i] = -c[i];
sort(c + 1, c + 1 + 2 * n);
for(int i = 1; i <= 2 * n; i ++) ans += abs(c[i] - c[n]); printf("%lld", ans);
return 0;
}
</details>