XMOJ3376 结界

很憨憨的题,想复杂了,是这场比赛四道题中难度第二的,可却是我唯一没做出来的题,挡了我的AK路。(发怒)(难得过一次最难题)

题目大意

有一个环,第 i i i 个位置上有 a i a_i ai 个物品,定义一次操作为将一个位置上的一个物品移动到其相邻两个位置之一,求最少的操作次数使得第 i i i 个位置上有 b i b_i bi 个物品。

n ≤ 1 0 5 n≤10^5 n105 a , b ≤ 1000 a,b≤1000 a,b1000

题解

先讲讲我赛时磕这题的心路历程。读完题发现 a , b a,b a,b 范围不大,所以一直在把复杂度往 a , b a,b a,b 上靠,然后一直往 dp 那个方向想,然后就憨完了。看了题解才发现很不牛,感觉如果 a , b a,b a,b 范围改成 1 0 9 10^9 109 赛时应该就会做了……

先考虑如果是一条链怎么做,很显然是贪心的去搞他,对于每个位置若是 a a a b b b 大就把多余部分弄出来,若是少就把多出来的部分搞进去。统计答案时只需要算一下每条边需要经过几个物品即可。(以上便是我赛时想出来的部分和正解的交集。)

再考虑变成环,其实也就是往1和n之间加了条边,我们假设这条边经过了 x x x 个物品(为正则从1到n方向,否则从n到1方向),那么1的物品数量就变成了 a 1 − x a_1-x a1x,1还需要的物品数量就是 b 1 − a 1 + x b_1-a_1+x b1a1+x(为正则给出去)。而此时只有2连向1的边能移动物品(因为1到n的边通过的物品数量已经被钦定好了),所以这条边所需要通过的物品数量就是 b 1 − a 1 + x b_1-a_1+x b1a1+x,然后2的物品数变成了 a 2 − b 1 + a 1 − x a_2-b_1+a_1-x a2b1+a1x,2还需要的物品数量就是 b 2 − a 2 + b 1 − a 1 + x b_2-a_2+b_1-a_1+x b2a2+b1a1+x,而此时只有3连向2的边能移动物品,所以这条边通过的物品数量就是 b 2 − a 2 + b 1 − a 1 + x b_2-a_2+b_1-a_1+x b2a2+b1a1+x……以此类推,我们可以算出每条边通过的物品数。

最后的答案就是每条边通过的物品数绝对值相加,发现每条边通过的物品数除了 x x x 以外都是一个常量,不妨设为 c i c_i ci,那么结果就是 ∑ ∣ c i + x ∣ = ∑ ∣ c i − ( − x ) ∣ \sum |c_i+x|=\sum |c_i-(-x)| ci+x=ci(x),然后就是很经典的问题,直接取中位数即可。

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

Code

代码巨简洁。

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
typedef long long ll;

int n,m,a[N],b[N],c[N];
ll ans;

int main(){
	freopen("border.in","r",stdin);
	freopen("border.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i],&b[i]);
	c[1]=0;
	for(int i=2;i<=n;i++)
		c[i]=c[i-1]+b[i-1]-a[i-1];
	sort(c+1,c+1+n);
	if(n&1) m=c[(n+1)/2];
	else m=(c[n/2]+c[n/2+1])/2;
	for(int i=1;i<=n;i++)
		ans+=abs(c[i]-m);
	printf("%lld",ans);
	return 0;
}

有一种输给了绿题的耻辱感,所以遇到这种数据范围开小了被误导的题有什么解决办法吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值