UNR #6 稳健型选手

神题。模拟赛搬了这个。

显然有一个 \(n^2q\) 的背包做法,考虑怎么继续优化。

sub1

首先贪心地,如果区间长度是奇数,那么最后一个数肯定最后一个取,然后变为长度偶数地情况

考虑将用 \(\rm 01\) 序列 \(b\) 表示所取得数。
合法的充要条件是 \(\forall i\sum\limits_{j=1}^ib_j\le \lfloor\frac{i}{2}\rfloor\)

考虑从前往后扫,用集合 \(s\) 维护已经取了的数,每次加进来两个数 \(x,y\),如果都取那么集合大小会变成 \(\frac{i}{2}+1\),所以再从集合中删掉最小的数。

优化至 \(O(nq\log n)\),可以获得 \(40'\)

sub2

上述贪心是从左边向右边扩展,我们考虑从右边向左边扩展。
转化充要条件: 前 \(2i\) 个取了 \(\le i\)\(\to\)\(2i\) 个取了 \(\ge i\) 个。
所以我们用集合 \(s\) 维护没有取得数,然后每次加进来 \(x,y\),现在集合大小为 \(\frac{i}{2}\),至少要再取一个,所以我们再将集合中最大的数删去。同样可以做到 \(O(nq\log n)\)

sub3

既然我们已经可以向左边和右边扩展,相当于可以在 \(O(\log n )\) 时间内向左边和右边扩展,使用不删除莫队可以做到 \(n\sqrt q\log n\)使用科技压位 tris/web tree 可以将这里的 \(log n\)变成 \(\log_w n\)\(\log\log n\) 就过了

sub4

可以向右和向左扩展,我们想到猫树分治,并发现这些信息可以合并。
使用主席树维护 \([l,mid]\) 的后缀取了哪些数,和 \([mid+1,r]\) 的前缀没取哪些数。然后我们发现可以左边已经取了的数不取,去取右边的数,可以二分 \(k\),满足左边第 \(k\) 小 <右边第 \(k\) 大。可以做到 \(O((n+q)\log^2 n)\)。可以继续优化,因为左边第 \(k\) 小不取,相当于同时取左边和右边 \(\ge x\) 的数,可以线段树二分变成 \(O(n\log^2n+q\log n)\)

code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
inline int read(){
	char ch=getchar();
	while(!isdigit(ch) && ch!='-') ch=getchar();
	int x=0,ff=1; if(ch=='-') ff=-1,ch=getchar();
	while(isdigit(ch)) x=(x<<3) + (x<<1) + (ch^48),ch=getchar();
	return x*ff;
}
const int N=2e5+5,inf=1e9;
int n,Qm,a[N],tot,rT[N]; LL sm[N];
struct qdate{
	int l,r;
} Q[N]; LL ans[N];
struct tnode{
	int lc,rc,c; LL s;
} tr[N<<6];
void Mdf(int &p,int pp,int L,int R,int kx){
	tr[p=++tot]=tr[pp]; tr[p].c++; tr[p].s+=kx;
	if(L==R) return ;
	int mid=(L+R)/2;
	if(kx<=mid) Mdf(tr[p].lc,tr[pp].lc,L,mid,kx);
	else Mdf(tr[p].rc,tr[pp].rc,mid+1,R,kx);
}
LL Qry(int p,int pp,int L,int R,int k){
	if(L==R) return 1ll*k*L;
	int mid=(L+R)/2; int rp=tr[p].rc,rpp=tr[pp].rc,bf=tr[rp].c+tr[rpp].c;
	if(bf>=k) return Qry(rp,rpp,mid+1,R,k);
	else return tr[rp].s+tr[rpp].s+Qry(tr[p].lc,tr[pp].lc,L,mid,k-bf);
}
LL solve(int l,int r){
	if(l==r) return a[l];
	LL res=0;
	if((r-l+1)%2==1) res+=a[r--];
	priority_queue<int,vector<int>,greater<int> > qt;
	for(int i=l;i<=r;i+=2){
		qt.push(a[i]); qt.push(a[i+1]);
		qt.pop();
	}
	while(!qt.empty()) res+=qt.top(),qt.pop();
	return res;
}
void solve(int L,int R,vector<int> q){
	if(q.empty()) return ;
	if(L==R){
		for(int i:q) ans[i]=a[L];
		return ;
	}
	int mid=(L+R)/2; vector<int> Lt,Rt,qq[2];
	for(int i:q){
		if(Q[i].r<=mid) Lt.push_back(i);
		else if(Q[i].l>mid) Rt.push_back(i);
		else qq[Q[i].l&1].push_back(i);
	}
	for(int o=0;o<2;o++){
		int nk=((mid&1)==o)?mid:mid-1;
		if(qq[o].empty()) continue;
		tot=0; int las=0;
		multiset<int> sh; sh.clear(); sh.insert(0);
		for(int i=nk;i>=L;i-=2){
			int u=a[i],v=a[i+1];
			if(u<v) swap(u,v);
			if(*sh.rbegin()>u){
				Mdf(rT[i],las,1,inf,*sh.rbegin());
				sh.erase(--sh.end());
				sh.insert(u); sh.insert(v);
			}
			else 
				sh.insert(v), Mdf(rT[i],las,1,inf,u);
			las=rT[i];
		}
		sh.clear(); sh.insert(inf); las=0;
		LL ss=0;
		for(int i=nk+2;i<R;i+=2){
			int u=a[i],v=a[i+1];
			if(u<v) swap(u,v);
			if(*sh.begin()<v){
				Mdf(rT[i],las,1,inf,*sh.begin());
				ss+=u+v-*sh.begin();
				sh.erase(sh.begin()); sh.insert(u); sh.insert(v);
			}
			else 
				Mdf(rT[i],las,1,inf,v),ss+=u,sh.insert(u);
			las=rT[i];  sm[i]=ss;
		}
		for(int i:qq[o]){
			int ql=Q[i].l,qr=Q[i].r;
			if((qr&1)==o) ans[i]+=a[qr--];
			--qr; ql=rT[ql];
			if(qr>nk) ans[i]+=sm[qr],qr=rT[qr];
			else qr=0;
			ans[i]+=Qry(ql,qr,1,inf,tr[ql].c);
		}
	}
	solve(L,mid,Lt); solve(mid+1,R,Rt);
}
int main(){
	n=read(); Qm=read();
	for(int i=1;i<=n;i++) a[i]=read();
	vector<int> q; q.clear();
	for(int i=1;i<=Qm;i++) Q[i]=(qdate){read(),read()},q.push_back(i);
	solve(1,n,q);
	for(int i=1;i<=Qm;i++) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2023-09-24 10:58  Shui_dream  阅读(74)  评论(0)    收藏  举报