bzoj-2527 Meteors

题意:

有一个星球,它的轨道被划分成m份,由n个国家占领;

一个国家可能占领多个轨道段;

现有q次事件,每次在一个轨道区间上每段落下Ai量的流星雨;

每个国家需要一定量的流星雨,当然它所得的为它占领的轨道段所得之和;

求每个国家所需的东西什么时候可以满足,如果始终不能满足输出NIE;


题解:

这题我第一眼看成了输出TAK或NIE;

结果仔细看看我选择死亡;

边处理事件边判断是否满足答案这个复杂度无论如何都无法接受;

所以要将事件离线下来处理;

另一个思路就是可持久化线段树;

建树的时候只会更改logm个结点,复杂度O(qlogm);

查询二分答案,复杂度O(n*logq*logm);

这样的时间复杂度是可以的啦,然而我们来算算空间;

线段树里要开qlogm个结点,两个long long占16字节;

算一下是80多兆了,似乎实现上就爆掉了?所以这个思路不可行;

我们并不能对时间预处理然后二分,那在二分的过程中处理事件;

只维护一个普通的线段树,那么执行一次二分是O(qlogqlogm)的;

然后复杂度O(nq*logqlogm)这比暴力还挫的复杂度。。

当然不是这样!我们把所有的n一起二分!

每次二分事件区间[l,r],将线段树调整至mid处;

然后统计所有的国家是否满足它的要求;

如果满足,则将其扔到[l,mid]再二分,满足则扔到[mid+1,r];

l==r记录一下答案就好啦!

复杂度O((q+n)logqlogm)?大概就是这么个东西;

好久不写数据结构写写还有点小激动呢2333;


代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 310000
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
using namespace std;
typedef long long ll;
struct node
{
	int no,need,head;
}c[N];
int next[N],to[N],ce;
int L[N],R[N],a[N];
int n,m,T;
ll sum[N<<2],cov[N<<2];
int ans[N];
bool vis[N];
void Pushup(int no)
{
	sum[no]=sum[no<<1]+sum[no<<1|1];
}
void Pushdown(int no,int len)
{
	if(cov[no])
	{
		cov[no<<1]+=cov[no];
		cov[no<<1|1]+=cov[no];
		sum[no<<1]+=cov[no]*(len-(len>>1));
		sum[no<<1|1]+=cov[no]*(len>>1);
		cov[no]=0;
	}
}
void update(int l,int r,int no,int st,int en,int val)
{
	if(st<=l&&r<=en)
	{
		cov[no]+=val;
		sum[no]+=(ll)val*(r-l+1);
	}
	else
	{
		Pushdown(no,r-l+1);
		int mid=l+r>>1;
		if(en<=mid)		update(lson,st,en,val);
		else if(st>mid)	update(rson,st,en,val);
		else	update(lson,st,en,val),update(rson,st,en,val);
		Pushup(no);
	}
}
ll query(int l,int r,int no,int x)
{
	if(l==r)
		return sum[no];
	else
	{
		Pushdown(no,r-l+1);
		int mid=l+r>>1;
		if(x<=mid)	return query(lson,x);
		else		return query(rson,x);
	}
}
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=c[x].head;
	c[x].head=ce;
}
void slove(int l,int r,int st,int en)
{
	if(st>en)	return ;
	if(l==r)
	{
		for(int i=st;i<=en;i++)
			ans[c[i].no]=l;
		return ;
	}
	int mid=l+r>>1;
	while(mid>T)
	{
		if(L[T+1]<=R[T+1])
			update(1,m,1,L[T+1],R[T+1],a[T+1]);
		else
			update(1,m,1,L[T+1],m,a[T+1]),
			update(1,m,1,1,R[T+1],a[T+1]);
		T++;
	}
	while(mid<T)
	{
		if(L[T]<=R[T])
			update(1,m,1,L[T],R[T],-a[T]);
		else
			update(1,m,1,L[T],m,-a[T]),
			update(1,m,1,1,R[T],-a[T]);
		T--;
	}
	int i,j,cnt;
	ll temp;
	for(i=st,cnt=0;i<=en;i++)
	{
		for(j=c[i].head,temp=0;j;j=next[j])
		{
			temp+=query(1,m,1,to[j]);
			if(temp>=c[i].need)
				break;
		}
		if(temp>=c[i].need)
			vis[i]=1,cnt++;
		else
			vis[i]=0;
	}
	for(i=st,j=st;i<=en;i++)
	{
		if(vis[i])
			swap(c[i],c[j++]);
	}
	slove(l,mid,st,st+cnt-1);
	slove(mid+1,r,st+cnt,en);
}
int main()
{
	int q,i,j,k,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
		scanf("%d",&x),add(x,i);
	for(i=1;i<=n;i++)
		scanf("%d",&c[i].need),c[i].no=i;
	scanf("%d",&q);
	for(i=1;i<=q;i++)
		scanf("%d%d%d",L+i,R+i,a+i);
	q++;
	L[q]=1,R[q]=m,a[q]=1e9;
	slove(1,q,1,n);
	for(i=1;i<=n;i++)
		if(ans[i]!=q)
			printf("%d\n",ans[i]);
		else
			puts("NIE");
	return 0;
}



你可能感兴趣的:(poi,线段树,离线,bzoj,整体二分)