#KTT#洛谷 9288 [ROI 2018] Innophone

题目

有一个二元函数 \(f(x,y)\),它是这么定义的:

\(f(x,y)=\left\{ \begin{array}{rcl} a, & & {\text{if} \quad \quad \ \ \ a \leq x}\\ b, & & {\text{else if} \quad b \leq y}\\ 0, & & {\text{else}} \end{array} \right.\)

其中 \(a,b\) 为常数。现在给定 \(n\) 组 \(x,y\),你需要选择合适的 \(a,b\),使得 \(\sum_{i=1}^{n} f(x_i,y_i)\) 最大。


分析

考虑将这 \(n\) 组 \(x,y\) 先按 \(x\) 从小到大排序,\(a\) 和 \(b\) 肯定分别是某个 \(x\) 以及某个 \(y\)。

固定 \(a\) 之后大于等于它的 \((x,y)\) 函数都是 \(a\),只需要考虑小于当前 \(a\) 的 \((x,y)\)。

如果选择了某个 \(b\),此时就是对大于等于 \(b\) 的 \(y\) 都能产生贡献,计个数为 \(x_c\),那么答案就是 \(bx_c+ma\)

可以发现答案是一个一次函数的形式甚至 \(ma\) 可以单独拎出来,而 \(x_c\) 只会越来越大,那么就可以用到 KTT。

将 \(y_i\) 离散化之后先查询,然后对 \(x_i=a\) 的 \([1,y_i]\) 整体加一,用 KTT 找到更优的截距,复杂度是三个 log 的。


代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N=150011;
typedef long long lll;
const lll inf=1e18;
lll ans=0;
int n,m,b[N]; pair<int,int>a[N];
struct Segment_Tree{
	lll K[N<<2],B[N<<2],bound[N<<2],lazy[N<<2];
	void pup(int k){
		bound[k]=min(bound[k<<1],bound[k<<1|1]);
		if (B[k<<1]<B[k<<1|1]||(B[k<<1]==B[k<<1|1]&&K[k<<1]<K[k<<1|1])){
			K[k]=K[k<<1|1],B[k]=B[k<<1|1];
			if (K[k<<1|1]<K[k<<1]) bound[k]=min(bound[k],(B[k<<1|1]-B[k<<1])/(K[k<<1]-K[k<<1|1]));
		}else{
			K[k]=K[k<<1],B[k]=B[k<<1];
			if (K[k<<1]<K[k<<1|1]) bound[k]=min(bound[k],(B[k<<1]-B[k<<1|1])/(K[k<<1|1]-K[k<<1]));
		}
	}
	void ptag(int k,lll z){
		B[k]+=K[k]*z,lazy[k]+=z;
		if (bound[k]!=inf) bound[k]-=z;
	}
	void pdown(int k){
		if (lazy[k]){
			ptag(k<<1,lazy[k]);
			ptag(k<<1|1,lazy[k]);
			lazy[k]=0;
		}
	}
	void build(int k,int l,int r){
		lazy[k]=0;
		if (l==r){
			K[k]=b[l],B[k]=0,bound[k]=inf;
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		pup(k);
	}
	void update(int k,int l,int r,int x,int y,lll z){
		if (l==x&&r==y&&z<=bound[k]){
			ptag(k,z);
			return;
		}
		int mid=(l+r)>>1;
		pdown(k);
		if (y<=mid) update(k<<1,l,mid,x,y,z);
		    else if (x>mid) update(k<<1|1,mid+1,r,x,y,z);
		        else update(k<<1,l,mid,x,mid,z),update(k<<1|1,mid+1,r,mid+1,y,z);
		pup(k);
	}
	lll query(int k,int l,int r,int x,int y){
		if (l==x&&r==y) return B[k];
		int mid=(l+r)>>1;
		pdown(k);
		if (y<=mid) return query(k<<1,l,mid,x,y);
			else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
				else return max(query(k<<1,l,mid,x,mid),query(k<<1|1,mid+1,r,mid+1,y));
	}
}Tre;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for (int i=1;i<=n;++i)
		cin>>a[i].first>>a[i].second,b[i]=a[i].second;
	sort(a+1,a+1+n),sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;++i)
		a[i].second=lower_bound(b+1,b+1+m,a[i].second)-b;
	Tre.build(1,1,m);
	for (int l=1,r;l<=n;l=r+1){
		ans=max(ans,1ll*(n-l+1)*a[l].first+Tre.query(1,1,m,1,m));
		for (r=l;r<=n&&a[r].first==a[l].first;++r)
			Tre.update(1,1,m,1,a[r].second,1);
		--r;
	}
	cout<<ans;
	return 0;
}
posted @ 2026-09-29 21:13  lemondinosaur  阅读(7)  评论(0)    收藏  举报