[CF1830F] The Third Grace

发布时间 2023-08-25 18:00:40作者: 灰鲭鲨

题目描述

You are given $ n $ intervals and $ m $ points on the number line. The $ i $ -th intervals covers coordinates $ [l_i,r_i] $ and the $ i $ -th point is on coordinate $ i $ and has coefficient $ p_i $ .

Initially, all points are not activated. You should choose a subset of the $ m $ points to activate. For each of $ n $ interval, we define its cost as:

  • $ 0 $ , if there are no activated points in the interval;
  • the coefficient of the activated point with the largest coordinate within it, otherwise.

Your task is to maximize the sum of the costs of all intervals by choosing which points to activate.

$ 1 \le t \le 10^5,1 \le n \le 10^6, 1 \le m \le 10^6,1 \le l_i \le r_i \le m,0 \le p_i \le 10^9 $

\(\sum n,\sum m\le 10^6\)

一个非常显然的 dp,定义 \(dp_i\)\(i\) 个数,选择了 \(i\) 的代价。那么枚举上一个数选在哪里,计算一下中间区间的贡献就行了。复杂度 \(O(n^2)\)

这个过程是可以用分块凸包维护,复杂度 \(O(n\sqrt n)\)

也可以用 KTT 维护,复杂度 \(O(log^3n)\)

然后讲官方做法。反过来,考虑一个 \(dp_i\) 对后面的 dp 的贡献。那么发现可以维护一个 \(h\),此时 \(dp_j=dp_i+h_jp_i\),同时要支持后缀加减 \(h\)

考虑李超树,由于 \(h\) 单调不降,所以同时之前已经加入了李超树的线段不会改变。那么这些线段因为 \(h\) 改变了,线段也要改成 \(k(h+c)-b-k*c\),然后打标记下传就可以了。新增的线段直接按照 \(h\) 来加入。但是注意到要把所有跨过那个后缀的所有线段下传到两边,所以复杂度 \(O(nlog^2n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long LL;
const LL INF=1e18;
int read()
{
	char ch=getchar();
	int s=0;
	while(ch<'0'||ch>'9')
		ch=getchar();
	while(ch>='0'&&ch<='9')
		s=s*10+ch-48,ch=getchar();
	return s;
}
struct node{
	int k;
	LL b;
	LL ask(int x)
	{
		return 1LL*k*x+b;
	}
}tr[N<<2];
int tg[N<<2],hl[N<<2],hr[N<<2],c[N],l,r,n,m,p[N];
vector<int>g[N];
LL ans=0;
void merge(int o,int x)
{
	int md=l+r>>1;
	tg[o]+=x;
	hl[o]+=x;
	hr[o]+=x;
	tr[o].b-=1LL*x*tr[o].k;
}
void pushdown(int o)
{
	if(tg[o]&&(o<<1|1)<=4*m)
	{
		merge(o<<1,tg[o]);
		merge(o<<1|1,tg[o]);
		tg[o]=0;
	}
}
void insert(int o,int l,int r,node x)
{
	if(x.b<-1e17)
		return;
	pushdown(o);
	int md=hl[o]==hr[o]? hl[o]:hr[o<<1];
	if(x.ask(md)>tr[o].ask(md))
		swap(tr[o],x);
	if(x.ask(hl[o])>tr[o].ask(hl[o]))
		insert(o<<1,l,md,x);
	if(x.ask(hr[o])>tr[o].ask(hr[o]))
		insert(o<<1|1,md+1,r,x);
}
void updtag(int o,int l,int r,int x,int y)
{
	if(l>=x)
	{
		merge(o,y);
		return;
	}
	int md=l+r>>1;
	pushdown(o);
	insert(o<<1,l,md,tr[o]);
	insert(o<<1|1,md+1,r,tr[o]);
	tr[o].k=0,tr[o].b=-INF;
	if(md>=x)
		updtag(o<<1,l,md,x,y);
	updtag(o<<1|1,md+1,r,x,y);
	hl[o]=hl[o<<1],hr[o]=hr[o<<1|1];
}
pair<int,LL> ask(int o,int l,int r,int x)
{
	if(l==r)
		return make_pair(hl[o],tr[o].ask(hl[o]));
	pushdown(o);
	int md=l+r>>1;
	pair<int,LL>ret;
	if(md>=x)
		ret=ask(o<<1,l,md,x);
	else
		ret=ask(o<<1|1,md+1,r,x);
	return make_pair(ret.first,max(ret.second,tr[o].ask(ret.first)));
}
int main()
{
	int T=read();
	while(T--)
	{
		n=read(),m=read();
		p[++m]=0;
		ans=0;
		for(int i=1;i<=m;i++)
			g[i].clear(),c[i]=0;
		for(int i=1;i<=4*m;i++)
			hl[i]=hr[i]=tg[i]=tr[i].k=0,tr[i].b=-INF;
		for(int i=1;i<=n;i++)
		{
			l=read(),r=read();
			g[l].push_back(r+1);
			c[r+1]++;
		}
		for(int i=1;i<m;i++)
			p[i]=read();
		for(int i=1;i<=m;i++)
		{
			LL dp=max(0LL,ask(1,1,m,i).second);
			for(int j=0;j<g[i].size();j++)
				updtag(1,1,m,g[i][j],1);
			updtag(1,1,m,i,-c[i]);
			if(i==m)
				printf("%lld\n",dp);
			insert(1,1,m,(node){p[i],dp});
		}
	}
}