C107 整体二分+树状数组(区修+点查)P3527 [POI2011] MET-Meteors

视频链接:C107 整体二分+树状数组(区修+点查)P3527 [POI2011] MET-Meteors_哔哩哔哩_bilibili

 

 

 

 

Luogu P3527 [POI2011] MET-Meteors

复制代码
// 整体二分+树状数组(区修+点查)O(n*logm*logk)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

#define N 300005
#define LL long long
#define lowbit(x) (x&-x)
int h[N],to[N],ne[N],tot;
void add(int a,int b){ //连边
  to[++tot]=b;ne[tot]=h[a];h[a]=tot;
}
int n,m,k,p,x[N],y[N],a[N],ans[N];
struct Q{
  int p,id; //国家希望陨石数,国家编号
};
vector<Q>q;

struct BIT{ 
  LL s[N<<1]; //维护2m段轨道的陨石数
  void add(int x,int v){ 
    while(x<=2*m) s[x]+=v,x+=lowbit(x);
  }
  LL sum(int x){
    LL t=0;
    while(x) t+=s[x],x-=lowbit(x);
    return t;
  }  
}tree; //树状数组

void solve(vector<Q>q,int L,int R){
  if(!q.size()) return;
  if(L==R){
    for(auto i:q) ans[i.id]=L; //记录答案
    return;
  }
  int mid=(L+R)>>1; //二分陨石雨次数
  vector<Q>q1,q2;   //分流国家序列
  for(int i=L;i<=mid;i++) //差分贡献
    tree.add(x[i],a[i]),tree.add(y[i]+1,-a[i]);
  for(auto i:q){
    LL s=0;         //累计国家i的陨石数
    for(int j=h[i.id];j&&s<=i.p;j=ne[j])
      s+=tree.sum(to[j])+tree.sum(to[j]+m);
    if(s>=i.p) q1.push_back(i);  //分流到左边
    else i.p-=s,q2.push_back(i); //分流到右边
  }
  for(int i=L;i<=mid;i++) //清空贡献
    tree.add(x[i],-a[i]),tree.add(y[i]+1,a[i]);
  solve(q1,L,mid);
  solve(q2,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m); //n个国家, m段轨道
  for(int i=1,o;i<=m;i++)
    scanf("%d",&o),add(o,i); //国家o向轨道i连边
  for(int i=1;i<=n;i++)
    scanf("%d",&p),q.push_back({p,i}); //希望陨石数
  scanf("%d",&k);
  for(int i=1;i<=k;i++) //[x,y]均落a个陨石
    scanf("%d%d%d",&x[i],&y[i],&a[i]);
  for(int i=1;i<=k;i++)
    if(y[i]<x[i]) y[i]+=m; //破环成链
  solve(q,1,k+1); //超过k次则被分裂到k+1节点
  for(int i=1;i<=n;i++)
    ans[i]<=k?printf("%d\n",ans[i]):puts("NIE");
}
复制代码

 

复制代码
// 整体二分+树状数组 O(n*logm*logk)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

#define N 300005
#define LL long long
#define lowbit(x) (x&-x)
int h[N],to[N],ne[N],tot;
void add(int a,int b){ //连边
  to[++tot]=b;ne[tot]=h[a];h[a]=tot;
}

int n,m,k,x[N],y[N],a[N],ans[N];
struct Q{
  int p,id; //国家希望陨石数,国家编号
}q[N],t[N];

struct BIT{ 
  LL s[N<<1]; //维护2m段轨道的陨石数
  void add(int x,int v){ 
    while(x<=2*m) s[x]+=v,x+=lowbit(x);
  }
  LL sum(int x){
    LL t=0;
    while(x) t+=s[x],x-=lowbit(x);
    return t;
  }  
}tree; //树状数组

void solve(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].id]=L;
    return;
  }
  int mid=(L+R)>>1;  //二分陨石雨次数
  int p1=l-1,p2=r+1; //分流国家序列
  for(int i=L;i<=mid;i++) //差分贡献
    tree.add(x[i],a[i]),tree.add(y[i]+1,-a[i]);
  for(int i=l;i<=r;i++){
    LL s=0;  //累计国家i的陨石数
    for(int j=h[q[i].id];j&&s<=q[i].p;j=ne[j])
      s+=tree.sum(to[j])+tree.sum(to[j]+m);
    if(s>=q[i].p) t[++p1]=q[i];  //分流到左边
    else q[i].p-=s,t[--p2]=q[i]; //分流到右边
  }
  for(int i=L;i<=mid;i++) //清空贡献
    tree.add(x[i],-a[i]),tree.add(y[i]+1,a[i]);
  for(int i=l;i<=p1;i++) q[i]=t[i];
  for(int i=p2;i<=r;i++) q[i]=t[r-i+p2]; //合流
  solve(l,p1,L,mid);
  solve(p2,r,mid+1,R); //分治
}
int main(){
  scanf("%d%d",&n,&m); //n个国家, m段轨道
  for(int i=1,o;i<=m;i++)
    scanf("%d",&o),add(o,i); //国家o向轨道i连边
  for(int i=1;i<=n;i++)
    scanf("%d",&q[i].p),q[i].id=i; //希望陨石数
  scanf("%d",&k);
  for(int i=1;i<=k;i++) //[x,y]均落a个陨石
    scanf("%d%d%d",&x[i],&y[i],&a[i]);
  for(int i=1;i<=k;i++)
    if(y[i]<x[i]) y[i]+=m; //破环成链
  solve(1,n,1,k+1); //超过k次则被分裂到k+1节点
  for(int i=1;i<=n;i++)
    ans[i]<=k?printf("%d\n",ans[i]):puts("NIE");
}
复制代码

 

posted @   董晓  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2023-03-27 G58 尼姆(Nim)游戏
点击右上角即可分享
微信分享提示