Gym102586B Evacuation

Evacuation

There are \(N+2\) towns in a straight line. The towns are numbered from \(0\) through \(N+1\) from left to right. Each town \(i\) (\(1 \leq i \leq N\)) has a shelter which can contain up to \(A_i\) people.

Currently, \(S\) people are traveling together and visiting one of the towns. However, you don't know which town they are visiting.

You just got to know that there are \(Q\) meteorites that can hit the towns. The \(i\)-th meteorite may strike towns \(L_i,L_i+1,\cdots,R_i\). To ensure the safety of the travelers, for each meteorite, you want to calculate the evacuation cost.

The evacuation cost for a meteorite is calculated as follows:

  • The evacuation cost is the minimum total cost needed to make all travelers safe no matter which town they are visiting.

  • A person is safe if he/she is in a shelter or a town outside the effect of the meteorite.

  • It takes \(1\) unit cost to move one person to an adjacent town (two towns are adjacent iff their numbers differ by exactly \(1\)).
    Note that only moving people costs money, and other actions (like accommodating people in a shelter) don't. It is guaranteed that towns \(0\) and \(N+1\) will never be affected by meteorites, so it is always possible to make all travelers safe.

Write a program that, for each meteorite, calculates the evacuation cost for that meteorite.

\(1 \leq N,Q \leq 2 \times 10^5\)

题解

有一个长度为\(n\)的序列\(a\)和一个常数\(s\).

\(q\)次询问, 每次给一个区间\([l,r]\), 询问\(\max_{l≤x≤r}f(l,r,x)\)的值.

其中\(f(l,r, x)\)被定义为, 对于所有满足以下条件的序列\(b\),

  • \(\sum^n_{i=1} b_i = s\)

  • \(∀l ≤ i ≤ r, b_i ≤ a_i\)

\(\sum^n_{i=1} |i − x|b_i\)的最小可能值

https://www.cnblogs.com/cjoierShiina-Mashiro/p/12800124.html

对于询问\(f(l,r,x)\),如果我们固定了\(x\),那么构造最优的\(b\)的方法是很简单的:

从小到大枚举\(i\in[0,+\infty)\),尽可能地让\(b_{x-i},b_{x+i}\)大。

如果在\(S\)还没有放完的时候遇到了一个\(pos\notin[l,r]\),那么把剩下的\(S\)全部放到\(b_{pos}\)就行了。

\(x\in[l,mid]\)那么\(pos=l-1\),若\(x\in(mid,r]\)那么\(pos=r+1\)

不难发现\(x\in[l,mid]\)\(f(l,r,x)\)\(r\)无关,\(x\in(mid,r]\)\(f(l,r,x)\)\(l\)无关。

因此设\(f(l,r,x)=\begin{cases}f(l,x)&x\in[l,mid]\\g(r,x)&x\in(mid,r]\end{cases}\)

预处理\(\{a_i\}\)的前缀和以及\(\{ia_i\}\)的前缀和之后可以\(O(1)\)的求出单点\(f,g\)

观察可得\(f,g\)满足四边形不等式。

考虑离线所有询问,对于一个形如\(\max\limits_{x\in[L,R]}(f\text{ or }g)(r,x)\)的询问,将其拆分成线段树上的\(O(\log n)\)个区间,最后对线段树的每个节点
用决策单调性优化暴力枚举\(x\)计算即可。

时间复杂度是\(O(n\log^2n)\),利用基数排序可以做到\(O(n\log n)\)

CO int N=2e5+10;
int n,q;
int64 s,s1[N],s2[N],ans[N];
int bound[N];

IN int64 sum(int64*s,int l,int r){
	return s[r]-s[l-1];
}
int find(int p){
	int l=0,r=min(n-p,p-1);
	while(l<r){
		int mid=(l+r+1)>>1;
		sum(s1,p-mid,p+mid)<=s?l=mid:r=mid-1;
	}
	return l;
}

IN int64 calc(int p,int len){
	if(len<=0) return 0;
	return sum(s1,p-len,p-1)*p-sum(s2,p-len,p-1)+sum(s2,p+1,p+len)-sum(s1,p+1,p+len)*p;
}
IN int64 calc(int o,int p,int lim){
	int f=!o?-1:1;
	int t=!o?max(lim-1,p-bound[p]-1):min(lim+1,p+bound[p]+1);
	return calc(p,f*(t-p)-1)+(s-(!o?sum(s1,t+1,2*p-(t+1)):sum(s1,2*p-(t-1),t-1)))*f*(t-p);
}

void solve(int o,int l,int r,vector<pair<int,int> >&q){
	if(q.empty()) return;
	if(l==r){
		for(CO pair<int,int>&a:q)
			ans[a.second]=max(ans[a.second],calc(o,l,a.first));
		return;
	}
	vector<pair<int,int> > ql,qr;
	int mid=q.size()/2,pos=0;
	int64 tmp,mx=0;
	for(int i=0;i<(int)q.size();++i)if(i!=mid)
		i<mid?ql.push_back(q[i]):qr.push_back(q[i]);
	for(int i=l;i<=r;++i)
		if((tmp=calc(o,i,q[mid].first))>mx) mx=tmp,pos=i;
	ans[q[mid].second]=max(ans[q[mid].second],mx);
	solve(o,l,pos,ql),solve(o,pos,r,qr);
}

vector<pair<int,int> > trans[4*N][2];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void insert(int x,int l,int r,int ql,int qr,int o,int id){
	if(ql>qr) return;
	if(ql<=l and r<=qr) return trans[x][o].push_back({!o?ql:qr,id});
	if(ql<=mid) insert(lc,l,mid,ql,qr,o,id);
	if(qr>mid) insert(rc,mid+1,r,ql,qr,o,id);
}
void query(int x,int l,int r){
	for(int i=0;i<2;++i){
		sort(trans[x][i].begin(),trans[x][i].end());
		solve(i,l,r,trans[x][i]);
	}
	if(l==r) return;
	query(lc,l,mid),query(rc,mid+1,r);
}
#undef lc
#undef rc
#undef mid

int main(){
	read(n),read(s);
	for(int i=1;i<=n;++i){
		int64 x=read<int64>();
		s1[i]=s1[i-1]+x,s2[i]=s2[i-1]+x*i;
	}
	for(int i=1;i<=n;++i) bound[i]=find(i);
	read(q);
	for(int i=1;i<=q;++i){
		int l=read<int>(),r=read<int>(),mid=(l+r)>>1;
		insert(1,1,n,l,mid,0,i),insert(1,1,n,mid+1,r,1,i);
	}
	query(1,1,n);
	for(int i=1;i<=q;++i) write(ans[i],'\n');
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/13399912.html