【瞎口胡】整体二分

整体二分是处理多个相似询问的利器。

例题 [POI2011]MET-Meteors

题意

有一个 \(n\) 个节点的环,每个节点都有一个所属国家。共有 \(n\) 个国家,第 \(i\) 个国家的陨石需求量为 \(p_i\)。

现在有 \(k\) 场陨石雨,每次陨石雨会落在 \([l,r]\) 区间内每一个节点上,并使这些节点的陨石数量增加 \(a\)。

询问每个国家至少在第几次陨石雨之后才能达到需求量,或者指出这不可能。

\(1 \leq n,m ,k \leq 3 \times 10^5,1 \leq p_i,a_i \leq 10^9\)

题解

先考虑一个询问是怎么样的。直接二分:如果 \([l,mid]\) 的陨石雨落下来之后达到了需求量,那答案一定 \(\leq mid\),清除 \([l,mid]\) 的陨石雨并递归 \([l,mid]\);否则不清除,递归 \([mid+1,r]\)。

\(n\) 个询问也是一样的。二分,把 \([l,mid]\) 的陨石雨落下来之后可以达到需求量的询问往 \([l,mid]\) 递归,反之往 \([mid+1,r]\) 递归。

因为最多递归 \(\log\) 次,所以复杂度是 \(O(n \log^2 n)\) 的(\(n,m,k\) 同阶,时间复杂度均以 \(n\) 表示)。

# include <bits/stdc++.h>
# define int long long
const int N=300010,INF=0x3f3f3f3f;

struct Node{
	int l,r,val,id;
}a[N<<1];
int opsum;
std::vector <int> idx[N];

int n,m,k;

int sum[N],need[N];
int q1[N],q2[N],q[N],ans[N];
inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline int lowbit(int x){
	return x&(-x);
}
inline void add(int x,int v){
	for(;x<=m;x+=lowbit(x))
		sum[x]+=v;
	return;
}
inline int query(int x){
	int ans=0;
	for(;x;x-=lowbit(x))
		ans+=sum[x];
	return ans;
}
inline int getsum(int x){
	int ans=0;
	for(int i=0;i<(int)idx[x].size();++i){
		ans+=query(idx[x][i]);
		if(ans>=need[x])
			return ans;
	}
	return ans;
}
void calc(int l,int r,int L,int R){
	if(L>R)
		return;	
		
	if(l==r){
		for(int i=L;i<=R;++i)
			ans[q[i]]=a[l].id;
		return;
	}
	int mid=(l+r)>>1;
	for(int i=l;i<=mid;++i)
		add(a[i].l,a[i].val),add(a[i].r+1,-a[i].val);

	int pL=0,pR=0;
	for(int i=L;i<=R;++i){
		int now=getsum(q[i]);
		if(now>=need[q[i]])
			q1[++pL]=q[i];
		else
			q2[++pR]=q[i],need[q[i]]-=now;
	}
	for(int i=1;i<=pL;++i)
		q[L+i-1]=q1[i];
	for(int i=1;i<=pR;++i)
		q[L+pL+i-1]=q2[i];
	for(int i=l;i<=mid;++i)
		add(a[i].l,-a[i].val),add(a[i].r+1,a[i].val);
	calc(l,mid,L,L+pL-1),calc(mid+1,r,L+pL,R);
	return;
}
signed main(void){
	n=read(),m=read();
	for(int i=1;i<=m;++i)
		idx[read()].push_back(i);
	for(int i=1;i<=n;++i)
		need[i]=read(),q[i]=i;
	k=read();
	for(int i=1;i<=k;++i){
		int l=read(),r=read(),val=read();
		if(l<=r){
			a[++opsum].l=l,a[opsum].r=r,a[opsum].val=val,a[opsum].id=i;
		}else{
			a[++opsum].l=l,a[opsum].r=m,a[opsum].val=val,a[opsum].id=i;
			a[++opsum].l=1,a[opsum].r=r,a[opsum].val=val,a[opsum].id=i; // 环形可以拆成两个
		}
	}	
	a[++opsum].l=1,a[opsum].r=m,a[opsum].id=k+1,a[opsum].val=1e9;
	calc(1,opsum,1,n);

	for(int i=1;i<=n;++i)
		if(ans[i]==k+1)
			puts("NIE");
		else
			printf("%lld\n",ans[i]);
	return 0;
}
上一篇:双指针 && 滑动窗口


下一篇:2020 ICPC 上海站(gym 102900) 题解