• 【洛谷3527】[POI2011] MET-Meteors(树状数组+整体二分)


    点此看题面

    大致题意: 一颗星球被分为(M)份,分别属于(N)个国家,有(K)场陨石雨,第(i)个国家希望收集(P_i)颗陨石,问其至少要在第几次陨石雨后才能达到目标。

    关于整体二分

    什么是整体二分

    其实我也不太清楚,反正就是一个很神仙的东西。

    而这题的做法听说就是传说中的整体二分。

    关于树状数组

    这题我一开始写的是线段树,结果代码又长又(TLE)

    改成树状数组后就过了。

    对于我之前说过的绝对不写树状数组,我只能说:真香。

    大致思路

    首先,我们将(K)场陨石雨(N)个询问全部用一个数组存储下来。

    然后我们对时间进行二分,每次将(lsim mid)场陨石雨能在第(mid)个操作前达成的询问放入左半区间,将其余的陨石雨和询问放入右半区间,然后继续操作即可。

    具体实现如下:

    • 先枚举陨石雨,将编号(le mid)的陨石雨全部用树状数组进行区间修改。
    • 然后枚举询问,枚举当前国家的每一份(可以使用邻接表)统计陨石个数和,然后与(P_i)比较即可。

    有一些小细节,还是见代码吧。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    #define Gmin(x,y) (x>(y)&&(x=(y))) 
    #define abs(x) ((x)<0?-(x):(x))
    #define swap(x,y) (x^=y^=x^=y)
    #define uint unsigned int
    #define LL long long
    #define ull unsigned long long
    #define INF 1000000000
    #define N 300000
    #define M 300000
    #define K 300000
    using namespace std;
    int n,m,k,t[N+5],lnk[N+5],nxt[M+5];
    struct key
    {
        int op,pos,val,l,r;
        key(int o=0,int p=0,int v=0,int x=0,int y=0):op(o),pos(p),val(v),l(x),r(y){}
    }s[N+K+5];
    class Class_FIO
    {
        private:
            #define Fsize 100000
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
            #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
            int f,FoutSize,Top;char ch,Fin[Fsize],*A,*B,Fout[Fsize],Stack[Fsize];
        public:
            Class_FIO() {A=B=Fin;}
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
            inline void write(int x) {if(!x) return pc('0');x<0&&(pc('-'),x=-x);while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
            inline void writec(char x) {pc(x);}
            inline void write_NoAnswer() {pc('N'),pc('I'),pc('E'),pc('
    ');}
            inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
    }F;
    class Class_DivideSolver
    {
        private:
            int ans[N+5];key ns1[N+K+5],ns2[N+K+5];
            class Class_TreeArray//树状数组
            {
                private:
                    #define lowbit(x) ((x)&-(x))
                    LL num[M+5];
                public:
                    inline void Update(int x,LL val) {while(x<=m) num[x]+=val,x+=lowbit(x);}
                    inline LL Query(int x,LL res=0) {while(x) res+=num[x],x-=lowbit(x);return res;}
            }T;
        public:
            inline void Solve(int l=1,int r=n+k,int L=1,int R=k+1)//整体二分
            {
                register int i,j,mid=L+R>>1,cnt1=0,cnt2=0;register LL tot;
                if(!(L^R)) {for(i=l;i<=r;++i) !s[i].op&&(ans[s[i].pos]=L);return;}//判边界
                for(i=l;i<=r;++i)//枚举操作
                {
                    if(s[i].op)//对于陨石雨
                    {
                        if(s[i].pos<=mid) (s[i].l<=s[i].r?(T.Update(s[i].l,s[i].val),T.Update(s[i].r+1,-s[i].val)):(T.Update(s[i].l,s[i].val),T.Update(1,s[i].val),T.Update(s[i].r+1,-s[i].val))),ns1[++cnt1]=s[i];//树状数组区间修改,并将其扔入左半部分
                        else ns2[++cnt2]=s[i];continue;//否则将其扔入右半部分
                    }
                    for(tot=0,j=lnk[s[i].pos];j;j=nxt[j]) if((tot+=T.Query(j))>=s[i].val) break;//统计陨石个数和,注意达成条件后直接break
                    tot>=s[i].val?ns1[++cnt1]=s[i]:(s[i].val-=tot,ns2[++cnt2]=s[i]);//比较tot与s[i].val,来判断将其扔入左半区间还是右半区间
                }
                for(i=1;i<=cnt1;++i) s[l+i-1]=ns1[i];for(i=1;i<=cnt2;++i) s[l+cnt1+i-1]=ns2[i];//更新序列
                for(i=l;i<=r;++i) s[i].op&&s[i].pos<=mid&&(s[i].l<=s[i].r?(T.Update(s[i].l,-s[i].val),T.Update(s[i].r+1,s[i].val)):(T.Update(s[i].l,-s[i].val),T.Update(1,-s[i].val),T.Update(s[i].r+1,s[i].val)),0);//清空树状数组
                cnt1&&(Solve(l,l+cnt1-1,L,mid),0),cnt2&&(Solve(l+cnt1,r,mid+1,R),0);//继续处理子区间
            }
            inline void Print() {for(register int i=1;i<=n;++i) ans[i]<=k?F.write(ans[i]),F.writec('
    '):F.write_NoAnswer();}//输出答案
    }D;
    int main()
    {
        register int i,x,y,z;
        for(F.read(n),F.read(m),i=1;i<=m;++i) F.read(x),nxt[i]=lnk[x],lnk[x]=i;
        for(i=1;i<=n;++i) F.read(t[i]);
        for(F.read(k),i=1;i<=k;++i) F.read(x),F.read(y),F.read(z),s[i]=key(1,i,z,x,y);//存储陨石雨
        for(i=1;i<=n;++i) s[i+k]=key(0,i,t[i]);//存储询问
        return D.Solve(),D.Print(),F.clear(),0;
    }
    
  • 相关阅读:
    计算机英语
    NSQ学习记录
    Java学习记录-注解
    VS插件开发

    双链表
    顺序表
    顺序队列
    顺序栈

  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3527.html
Copyright © 2020-2023  润新知