解题报告:CF439D

Posted tlx-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解题报告:CF439D相关的知识,希望对你有一定的参考价值。

题目链接:CF439D Devu and his Brother
太困肝了一晚上也没肝出来,被大佬虐爆了。
考虑这个结论:如果需要操作才能实现要求,那这个数一定是(a),(b)中的数。
为什么呢,你可以尝试设此数为(k),写出其函数解析式,则一定是一次函数,单调性明显,一定出现在两端点上。
考虑对答案有贡献:
显然是(a)数组中(<k)(b)数组中(>k)的数才有贡献,这时我们直接枚举每一个数,然后二分查找边界值,当然要处理出前缀和,为什么呢?
(ps):下面的数组都是从小到大排序意义上的:
比如说我们要把(max_a)(min_b)都趋近于(k),这时(a)数组中前(x)个数(<k)(b)数组中(b_y)(b_m>k),那么我们记(sum_{a/b,i})为前缀和,则贡献为:

[kx-sum_{a,x}+sum_{b,m}-sum_{b,y-1}-k(m-y+1) ]

记得开(long;long),这样复杂度就是(O((n+m)log(n+m))),可以通过本题。
令外讨论区还有三分的做法,笔者不会,所以有兴趣就去讨论区看吧(咕咕~

(Code):

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=100005;
typedef long long ll;
const ll inf=1ll<<61;
int a[MAXN],b[MAXN];
ll suma[MAXN],sumb[MAXN],ans=inf;
int mina=2147483647,maxb=0;
int n,m;
int mm;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),mina=min(mina,a[i]);
	for(int i=1;i<=m;i++) scanf("%d",&b[i]),maxb=max(maxb,b[i]);
	if(mina>=maxb){
		printf("%d
",0);
		return 0;
	}
	sort(a+1,a+n+1),sort(b+1,b+m+1);
	for(int i=1;i<=n;i++) suma[i]=suma[i-1]+(ll)a[i];
	for(int i=1;i<=m;i++) sumb[i]=sumb[i-1]+(ll)b[i];
	for(int i=1;i<=n;i++){
		if(a[i]>=b[m]) break;
		int l=1,r=m,mid;
		while(1){
			mid=(l+r)>>1;
			if(l>=r) break;
			if(b[mid]<=a[i]) l=mid+1;
			else r=mid;
		}
		ll now=(ll)a[i]*(ll)(i-1)-suma[i-1];
		if(mid>0) now=now+sumb[m]-sumb[mid-1]-(ll)a[i]*(ll)(m-mid+1);
		if(now<ans) ans=now,mm=a[i];
	}
	for(int i=1;i<=m;i++){
		if(b[i]<a[1]) continue;
		int l=1,r=n,mid;
		while(1){
			mid=(l+r)>>1;
			if(l>=r) break;
			if(a[mid]<=b[i]) l=mid+1;
			else r=mid;
		}
		mid-=1;
		if(b[i]>=a[n]) mid=n;
		ll now=sumb[m]-sumb[i]-(ll)b[i]*(ll)(m-i);
		now=now+(ll)b[i]*(ll)mid-suma[mid];
		if(ans>now) ans=now,mm=b[i];
	}
	printf("%lld
",ans);
	return 0;
} 

大佬xxx:你好傻啊,这么麻烦,其实核心代码只有一行,这么(naive)的贪心都不会。
我:???
索性盗了一份代码,谁会证明呢,大佬可以在讨论群秀一秀?

(Code)(神仙贪心):

#include <bits/stdc++.h>
#define gc() getchar() 
#define LL long long
#define rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rep(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
const int N = 1e5 + 5;
int n, m, a[N], b[N];

inline int read() {
	int x = 0;
	char ch = gc();
	while (!isdigit(ch)) ch = gc();
	while (isdigit(ch)) x = x * 10 + ch - ‘0‘, ch = gc();
	return x; 
}

inline bool cmp(int x , int y) {
	return x > y;	
}

int main() {
	n = read(), m = read();
	rep(i, 1, n) a[i] = read();
	rep(i, 1, m) b[i] = read();
	sort(b + 1, b + m + 1, cmp);
	sort(a + 1, a + 1 + n);
	if (n > m) n = m;
	LL ans = 0;
	rep(i, 1, n) if (a[i] < b[i]) ans += 1LL * (b[i] - a[i]);
	printf("%lld
", ans);
	return 0;
} 

这样的复杂度是(O(nlog n+mlog m+n+m)),比我的快多了,%%%。

以上是关于解题报告:CF439D的主要内容,如果未能解决你的问题,请参考以下文章

CF1605F PalindORme 解题报告

cf1206解题报告

cf1199解题报告

# cf1187解题报告

CF1082解题报告

CF 1215解题报告