码迷,mamicode.com
首页 > 其他好文 > 详细

解题报告:CF439D

时间:2020-03-28 10:23:18      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:efi   cst   c++   har   https   一次函数   bre   直接   二分   

题目链接: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\n",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\n",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\n", ans);
	return 0;
} 

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

解题报告:CF439D

标签:efi   cst   c++   har   https   一次函数   bre   直接   二分   

原文地址:https://www.cnblogs.com/tlx-blog/p/12585836.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!