题目描述

nn个线性序列,第ii个序列可以表示成ki×x+bik_i×x+b_i的形式(x=0,1,2,…)。

现在询问将这些序列的数从小到大合并起来,前mm个数的和是多少(重复出现的数合并后也会多次出现)。

对于100%100\%的数据,保证1n100000,1m100000,1ki,bi10001≤n≤100000,1≤m≤100000,1≤k_i,b_i≤1000

分析

二分答案

寻找nn个序列的数从小到大合并起来的最大值MM

判断nn个序列中值<=M<=M的个数总和是否<=m<=m

alt

计算个数总和

序列满足ki×x+bi<=Mk_i×x+b_i<=M条件的xx个数为

Mbiki+1\lfloor\frac{M-b_i}{k_i}\rfloor+1

计算数值总和

等差数列求和

(bi+bi+(num1)ki)num2\frac{(b_i+b_i+(num-1)*k_i)*num}{2}

结果

假设找到的边界值MM计算个数为kk

那么M+1M+1一定存在且计算个数一定大于mkm-k

所以最终结果为sum(M)+(M+1)(mk)sum(M)+(M+1)*(m-k)

边界

最小值为0

最大值为105103+10310^5*10^3+10^3

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,m,k[maxn],b[maxn];
ll calc(int x)
{
	ll res=0;
	for(int i=1;i<=n;i++){
		if(x>=b[i]) res+=(x-b[i])/k[i]+1;
	}
	return res;
}
ll sum(int x){
	ll res=0;
	for(int i=1;i<=n;i++)
	{
		if(x>=b[i]) {
			int num=(x-b[i])/k[i]+1;
			res+=1LL*(b[i]+b[i]+(num-1)*k[i])*num/2;
		}
	}
	return res;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&k[i],&b[i]);
	scanf("%d",&m);
	int l=0,r=1e9;
	while(l+1<r){
		int mid=(l+r)/2;
		if(calc(mid)<=m)
			l=mid;
		else
			r=mid;
	}
	printf("%lld",sum(l)+1LL*(l+1)*(m-calc(l)));
}