【[POI2011]MET-Meteors】整体二分+一些神奇优化

洛谷上很早就过了,然而NK上的数据很强,迫不得已重构了两次代码orz,还调整了一下算法。
P3932Meteors
时间限制 : - MS   空间限制 : 265536 KB
评测说明 : 3S
luogu3752
问题描述
Byteotian Interstellar Union有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。 这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。 BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。
输入格式
输入: 第一行是两个数N,M。 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。 第四行有一个数K,表示BIU预测了接下来的K场陨石雨。 接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,…,Ri,否则就是Ri,Ri+1,…,m-1,m,1,…,Li),向区间中的每个太空站提供Ai单位的陨石样本。
输出格式
输出: N行。 第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。 如果到第K波结束后仍然收集不到,输出NIE。
样例输入
3 5 1 3 2 1 3 10 5 7 3 4 2 4 1 3 1 3 5 2
样例输出
3 NIE 1
提示
 1<=n,m,k<=3*10^5 1<=Pi<=10^9 1<=Ai<10^9

来源  POI2011
这道题有个十分显然的做法,就是直接套入一个树状差分数组进行整体二分,然后每次加入前mid个,查询,搞完还原树状数组。看上去时间复杂度并没有什么问题,然后,愉快的被卡卡卡卡常(微笑)。
这时候就要用到一个类似于莫队里面的左移右移的东西了。我们可以将下陨石和询问分开看,设定一个类似莫队R的 东西(本题是前缀和没有L),对R跑到当前mid的位置。这样我们可以尝试去画一个图,就会惊讶发现,对于整个整体二分最多每层会移动n次。
看似对时间复杂度影响不大,其实不然,实际上我们反复用了上一次的插入树状数组后的结果,也就是说,我们节省了很多重复的,冗杂的工作,最后写出代码也其实更加精简易懂(不用处理队列类型)。
code:
#include<stdio.h>
#include<bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
using namespace std;
const int maxn = 300005;
int n,m,k;
int bit[maxn];
vector<int>ve[maxn];
void add(int x,int d) { for(;x<=m+2;x+=lowbit(x)) bit[x]+=d; }
int getsum(int x) { int sum=0; for(;x;x-=lowbit(x)) sum+=bit[x]; return sum; }
int ans[maxn];
struct node
{ int id,ned; };
struct chan
{
	int l,r,cha;
}z[maxn];
void modify(int po,int knd)
{
	if(knd)
	{
		if(z[po].r>=z[po].l)
		{
			add(z[po].l,z[po].cha); 
			add(z[po].r+1,-z[po].cha);
		}
		else
		{
			add(1,z[po].cha);
			add(z[po].r+1,-z[po].cha);
			add(z[po].l,z[po].cha);
		}
	}
	else
	{
		if(z[po].r>=z[po].l)
		{
			add(z[po].l,-z[po].cha); 
			add(z[po].r+1,z[po].cha);
		}
		else
		{
			add(1,-z[po].cha);
			add(z[po].r+1,z[po].cha);
			add(z[po].l,-z[po].cha);
		}
	}
}
int cur;
void query(vector<node>q,int l,int r)
{
	if(!q.size()) return;
	if(l==r)
	{
		int ss = q.size();
		for(int i=0;i<ss;i++) ans[q[i].id] = l;
		return;
	}
	int mid = (l+r)/2;
	while(cur<mid) ++cur,modify(cur,1);
	while(cur>mid) modify(cur,0),--cur;
	int ss=q.size();
	vector<node>q1,q2;
	for(int i=0;i<ss;i++)
	{
		int x = q[i].id;
		int  sd = ve[x].size();
		int summ = 0; 
		for(int j=0;j<sd;j++)
		{
			summ+=getsum(ve[x][j]);
			if(summ>=q[i].ned) break;
		}
		if(summ>=q[i].ned) q1.push_back(q[i]);
		else q2.push_back((node){q[i].id,q[i].ned});
	}
	query(q1,l,mid);
	query(q2,mid+1,r);
}
int main()
{
	vector<node>q;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&x);
		ve[x].push_back(i);
	}
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		q.push_back((node){i,x});
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++)
	{
		int ll,rr,aa;
		scanf("%d%d%d",&ll,&rr,&aa);
		z[i].l = ll ;z[i].r = rr; z[i].cha = aa;
	}
	++k; z[k].l=1; z[k].r=m; z[k].cha = 0x3f3f3f3f;
	query(q,1,k);
	for(int i=1;i<=n;i++) if(ans[i]!=k)  printf("%d\n",ans[i]);
	else printf("NIE\n"); 
}
 
posted @ 2018-06-16 02:01  Newuser233  阅读(5)  评论(0)    收藏  举报