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>
posted @ 2021-07-20 10:33  Saintex  阅读(143)  评论(0编辑  收藏  举报