整体二分

抽象。

P3527 [POI2011] MET-Meteors

Byteotian Interstellar Union 有 \(n​\) 个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为 \(m​\) 份(第 \(m​\) 份和第 \(1​\) 份相邻),第 \(i​\) 份上有第 \(a_i​\) 个国家的太空站。

这个星球经常会下陨石雨。BIU 已经预测了接下来 \(k\) 场陨石雨的情况。

BIU 的第 \(i\) 个成员国希望能够收集 \(p_i\) 单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

sol

如果每个数的二分,显然特别好写,但是复杂度过于高昂。

所以要思考一种极快速的方法。注意到二分没办法进一步优化,所以考虑优化“一个数二分”,载一大堆数一起二分。这样说可能很抽象。

考虑一个函数 \(work(l,r,ql,qr).\)

函数表示欲要处理 \(ans[a[l]],ans[a[l+1]],ans[a[l+2]],\cdots,ans[a[r]].\)

现在已经可以确定这些 \(ans\) 是可以在 \(ql,qr\) 之间的。

然后考虑把整个区间 \(a\) 划分成两个区间 \([l,x],(x,r],\) 然后这两个区间的 \(ans\) 分别可以确定在 \([ql,qmid],(qmid,qr]\) 之间。然后继续递归下去。

先降下 \([ql,qmid]\) 之间的流星雨,然后处理一下流星雨的树状数组,逐个国家判断之的流星雨降落数量和 \(p[a[i]]\) 的关系。

然后如果小于用一个指针划分到左边,否则用指针划分到右边。

最后注意 \(l\) 的区间分治过程中要回溯(把之前降下的流星雨给升回去)。

然后最后为了判断无解情况要多加一次流星雨。

程式
#include <bits/stdc++.h>
#define Add emplace_back
typedef long long ll;
using std::cin;
using std::cout;
const int N=3e5+10;
ll a[N],b[N],c[N];
int p[N],ans[N];
int n,m,k;
inline int lb(int x){ return x&-x; }
inline void add(int x,int k){ while(x<=m) c[x]+=k, x+=lb(x); }
inline ll qry(int l){
	ll ans=0;
	while(l) ans+=c[l], l-=lb(l);
	return ans;
}
inline void addr(int l,int r,int k){ add(l,k), add(r+1,-k); }
struct rrr{ int l,r,v; } rain[N];
inline void upd(int i,int zf){
	if(rain[i].l<=rain[i].r) addr(rain[i].l,rain[i].r,rain[i].v*zf);
	else addr(1,rain[i].r,rain[i].v*zf), addr(rain[i].l,m,rain[i].v*zf);
}
std::vector<int> v[N];
void work(int l,int r,int ql,int qr){
	if(ql==qr||l>r) {
		for(int i=l;i<=r;++i) ans[a[i]]=ql;
		return;
	}
	int midc=(l+r)>>1,midq=(ql+qr)>>1;
	for(int i=ql;i<=midq;++i) upd(i,1);
	int lft=l,rgt=r;
	for(int i=l;i<=r;++i){
		ll R=0;
		for(int x:v[a[i]]){
			R+=qry(x);
			if(R>=p[a[i]]) break;
		}
		if(R>=p[a[i]]) b[lft++]=a[i];
		else b[rgt--]=a[i];
	}
	for(int i=l;i<=r;++i) a[i]=b[i];
	work(lft,r,midq+1,qr);
	for(int i=ql;i<=midq;++i) upd(i,-1);
	work(l,rgt,ql,midq);
}
// l r country ql qr rain
int main(){
	std::ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin>>n>>m;
	for(int i=1,tmp;i<=m;++i) cin>>tmp, v[tmp].Add(i);
	for(int i=1;i<=n;++i) cin>>p[i],a[i]=i;
	cin>>k;
	for(int i=1;i<=k;++i) cin>>rain[i].l>>rain[i].r>>rain[i].v;
	work(1,n,1,k+1);
	for(int i=1;i<=n;++i){
		if(ans[i]>k) cout<<"NIE\n";
		else cout<<ans[i]<<"\n";
	}
	return 0;
}
posted @ 2024-05-07 16:58  ChihiroFujisaki  阅读(14)  评论(0)    收藏  举报