P7424 [THUPC2017] 天天爱射击

【题意】

给定n个木板的在x轴上的位置,以及每个木板能经受的了几次射击,还有m颗子弹的射击位置(沿y轴方向),问每次射击后共有几个木板坏了

【分析】

我们考虑每块木板会在什么时候坏,较为暴力的考虑可以对于每个木板进行一次二分,时间复杂度$O(n^2logn)$

继续考虑,如果对子弹按照坐标排序,那么我们对于每个木板(x1,x2)可以找到对应的会射到这块木板的子弹区间[l,r],然后维护一个主席树可以做到$O(nlogn)$,但是由于常数较大,被整体二分吊打

下面介绍整体二分做法:

solve(l,r,L,R)表示现在子弹区间为[l,r],对应的木板(询问)为[L,R]

然后我们把l-mid的修改加入到树状数组中,记录坐标为1-i 的点会被射到几次

然后对于区间L-R的询问依次走一遍,把被射穿的分到左侧,没有射穿的减去被射击次数,分到右侧

注意这里要重新把树状数组减下去,方便后续操作

 

【代码】

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,m;
struct board
{
    int l,r,s,id;
}b[maxn],b1[maxn],b2[maxn];
int a[maxn],ans[maxn],pos[maxn];
int c[maxn],maxi;
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int y)
{
    for(int i=x;i<=maxi;i+=lowbit(i))
        c[i]+=y;
}
int query(int x)
{
    int res=0;
    for(int i=x;i>0;i-=lowbit(i))
        res+=c[i];
    return res;
}
void solve(int l,int r,int L,int R)
{
    if(L>R) return;
    if(l==r)
    {
        for(int i=L;i<=R;i++) pos[b[i].id]=l;
        return;
    }
    int mid=(l+r)>>1;
    for(int i=l;i<=mid;i++) add(a[i],1);
    int cnt1=0,cnt2=0;
    for(int i=L;i<=R;i++)
    {
        int k=query(b[i].r)-query(b[i].l-1);
        if(k>=b[i].s) b1[++cnt1]=b[i];
        else b[i].s-=k,b2[++cnt2]=b[i];
    }
    for(int i=1;i<=cnt1;i++) b[i+L-1]=b1[i];
    for(int i=1;i<=cnt2;i++) b[i+L+cnt1-1]=b2[i];
    for(int i=l;i<=mid;i++) add(a[i],-1);
    solve(l,mid,L,L+cnt1-1);
    solve(mid+1,r,L+cnt1,R);
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].s);
        b[i].id=i; maxi=max(maxi,b[i].r);
    }
    for(int i=1;i<=m;i++) scanf("%d",&a[i]);
    solve(1,m+1,1,n);
    for(int i=1;i<=n;i++) ans[pos[i]]++;
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

 

posted @ 2021-05-21 14:33  andyc_03  阅读(1248)  评论(0)    收藏  举报