您的位置:首页 > 其它

【BZOJ2527】【POI2011】Meteors [整体二分]

2017-02-23 11:29 399 查看

Meteors

Time Limit: 60 Sec  Memory Limit: 128 MB
[Submit][Status][Discuss]

Description

  这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。
  BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

Input

  第一行是两个数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单位的陨石样本。

Output

  输出N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。

Sample Input

  3 5
  1 3 2 1 3
  10 5 7
  3
  4 2 4
  1 3 1
  3 5 2

Sample Output

  3
  NIE
  1

HINT

   1<=n,m,k<=3*10^5 , 1<=Ai,Pi<=10^9

Main idea

  每个国家有一个需要价值,一个国家可以控制多个点,定义国家已经获得的价值为每个控制点上的价值和,每次操作可以将一段区间上每个点都加上一个价值,问每个国家在第几个操作时达到了需要价值,若达不到则输出NIE。

Solution

  我们先从二分层面去考虑,对于一个点来说,可以二分答案来求解,那么我们就可以利用整体二分。

  整体二分,就是我们将所有的询问一起来做,然后二分操作区间L,R,执行L,MID的部分,

  然后判断询问是否可行,如果可行,将这个询问放到左边区间,否则加上左边的价值再把这个询问放到右区间。然后继续递归操作区间。

  对于这道题来说,由于一个国家可以控制多个点,我们用链表来存,然后用Bit来判断是否可行。

  这样就解决了这道题\(≧▽≦)/。

Code

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<map>
using namespace std;

typedef long long s64;

const int ONE=300005;
const int INF=2147483640;

int n,m,k;
int x;
int next[ONE],first[ONE],go[ONE],tot;
int Ans[ONE];

struct power
{
int need;
int id;
}a[ONE],qL[ONE],qR[ONE];

struct opera
{
int l,r;
int val;
}oper[ONE];

int get()
{
int res=1,Q=1;char c;
while( (c=getchar())<48 || c>57 )
if(c=='-')Q=-1;
res=c-48;
while( (c=getchar())>=48 && c<=57 )
res=res*10+c-48;
return res*Q;
}

void Add(int u,int v)
{
next[++tot]=first[u];   first[u]=tot;   go[tot]=v;
}

namespace Bit
{
struct power
{
s64 value;
}Node[ONE];

int lowbit(int i)
{
return i&-i;
}

void Update(int R,int x)
{
for(int i=R;i<=m;i+=lowbit(i))
Node[i].value+=x;
}

s64 Query(int R)
{
s64 res=0;
for(int i=R;i>=1;i-=lowbit(i))
res+=Node[i].value;
return res;
}
}

void Update(int l,int r,int val)
{
if(l>r)
{
Bit::Update(l,val); Bit::Update(m+1,-val);
Bit::Update(1,val); Bit::Update(r+1,-val);
}
else
{
Bit::Update(l,val); Bit::Update(r+1,-val);
}
}

void Solve(int l,int r,int L,int R)//l,r 询问;L,R 操作
{
if(l>r) return;
if(L==R)
{
for(int i=l;i<=r;i++)
Ans[a[i].id] = L;
return;
}

int M=(L+R)>>1;
for(int i=L;i<=M;i++)
Update(oper[i].l, oper[i].r, oper[i].val);

int l_num=0,r_num=0;
for(int i=l;i<=r;i++) //判断询问在哪个区间
{
s64 sum=0;
for(int e=first[a[i].id];e;e=next[e])
{
sum+=Bit::Query(go[e]);
if(sum>=a[i].need) break;
}

if(sum>=a[i].need)
{
qL[++l_num]=a[i];
}
else
{
qR[++r_num]=a[i];
qR[r_num].need-=sum;
}
}
int t=l;
for(int i=1;i<=l_num;i++) a[t++]=qL[i];
for(int i=1;i<=r_num;i++) a[t++]=qR[i];

for(int i=L;i<=M;i++)
Update(oper[i].l, oper[i].r, -oper[i].val);

Solve(l,l+l_num-1,L,M);
Solve(l+l_num,r,M+1,R);
}

int main()
{
n=get();    m=get();
for(int i=1;i<=m;i++) {x=get(); Add(x,i);}
for(int i=1;i<=n;i++)
{
a[i].need=get();
a[i].id=i;
}

k=get();
for(int i=1;i<=k;i++)
{
oper[i].l=get();    oper[i].r=get();    oper[i].val=get();
}

Solve(1,n,1,k+1);

for(int i=1;i<=n;i++)
{
if(Ans[i] > k) printf("NIE");
else printf("%d",Ans[i]);
printf("\n");
}
}
View Code

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: