• 分块


    一、Contest Hunter Round #46 磁力块

    描述:
    在一片广袤无垠的原野上,散落着N块磁石。每个磁石的性质可以用一个五元组(x,y,m,p,r)描述,其中x,y表示其坐标,m是磁石的质量,p是磁力,r是吸引半径。若磁石A与磁石B的距离不大于磁石A的吸引半径,并且磁石B的质量不大于磁石A的磁力,那么A可以吸引B。
    小取酒带着一块自己的磁石L来到了这篇原野的(x0,y0)处,我们可以视为磁石L的坐标为(x0,y0)。小取酒手持磁石L并保持原地不动,所有可以被L吸引的磁石将会被吸引过来。在每个时刻,他可以选择更换任意一块自己已经获得的磁石(当然也可以是自己最初携带的L磁石)在(x0,y0)处吸引更多的磁石。小取酒想知道,他最多能获得多少块磁石呢?
    输入格式
    第一行五个整数x0,y0,pL,rL,N,表示小取酒所在的位置,磁石L磁力、吸引半径和原野上散落磁石的个数。
    接下来N行每行五个整数x,y,m,p,r,描述一块磁石的性质。 输出格式
    输出一个整数,表示最多可以获得的散落磁石个数(不包含最初携带的磁石L)。

    思路:

    容易想到:可以用BFS的思想来统计答案,对于一个磁石,所有他能够吸引的全部加入queue,直到queue为空。

    现在我们只需考虑如何快速的判断一块磁石能否被吸引就好了。条件为:质量<=磁力,距离<=吸引半径。

    我们考虑分块来解决:

    先按质量排序,分成√n 段。然后每一段再按照距离排序。

    那么对于每一块用来吸引的磁石(设为x),依次扫描这√n段,假设到第i段时,这一段的mmax大于R[x],停止。

    此时,

    对于前i-1段,必定满足第一个条件,对于这些段的,直接从左往右扫,根据距离关系判断一下就好,那么复杂度均摊O(1),有i-1块则总复杂度为O(√n)。

    对于第i段,暴力扫描整段,满足条件的加入答案并打上标记(避面重复),复杂度O(√n)。

    所以,整个算法复杂度为O(n√n)。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline 
    using namespace std;
    
    IL int gi () {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=250010;
    const int Sqn=510;
    const double eps=1e-6;
    
    int n,Num,len,dir,ans;
    
    struct STONE {
        double R,dis; 
        int x,y,m,p,vis;
    }sto[N];
    struct BLOCK {
        STONE s[Sqn];
        int head,num,Max;
    }blo[Sqn];
    IL double calc (STONE a,STONE b) {
        return (double)sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
    }
    IL bool cmp1 (STONE a,STONE b) {return a.m<b.m;}
    IL bool cmp2 (STONE a,STONE b) {return  a.dis<b.dis;}
    
    queue <STONE> q;
    
    int main ()
    {
        double R;
        RG int i,j,x,y,m,p;
        x=gi(),y=gi(),p=gi();scanf ("%lf",&R);
        sto[0]=(STONE){R,0,x,y,0,p,0};
        n=gi(),len=sqrt(n),Num=n/len+(n%len!=0);
        for (i=1;i<=n;++i) {
            x=gi(),y=gi(),m=gi(),p=gi();scanf ("%lf",&R);
            sto[i]=(STONE){R,0,x,y,m,p,0};
            sto[i].dis=calc(sto[i],sto[0]);
        }
        sort (sto+1,sto+n+1,cmp1);
        for (i=1;i<=Num;++i) {
            dir=(i-1)*len;
            blo[i].head=1,blo[i].num=0;
            for (j=1;j<=len&&dir+j<=n;++j) 
                blo[i].s[++blo[i].num]=sto[dir+j];   
            blo[i].Max=blo[i].s[blo[i].num].m;
            sort (blo[i].s+1,blo[i].s+blo[i].num+1,cmp2);
        }
        q.push(sto[0]);
        while (!q.empty()) {
            RG STONE now=q.front(); q.pop();
            for (i=1;i<=Num&&now.p>=blo[i].Max;++i)
                while (now.R+eps>=blo[i].s[blo[i].head].dis&&blo[i].head<=blo[i].num) {
                    if (!blo[i].s[blo[i].head].vis) 
                        ++ans,blo[i].s[blo[i].head].vis=1,q.push(blo[i].s[blo[i].head]);
                    ++blo[i].head;
                }
            dir=i;
            if (dir>Num) continue; 
            for (i=blo[dir].head;i<=blo[dir].num;++i)
                if (blo[dir].s[i].vis==0&&now.p>=blo[dir].s[i].m&&now.R+eps>=blo[dir].s[i].dis)
                ++ans,blo[dir].s[i].vis=1,q.push(blo[dir].s[i]);
        }
        printf ("%d
    ",ans);
        return 0;
    }
    BY BHLLX

     二、LuoGu P4186 蒲公英

    一句话题意:在线求区间众数。

    这里给出的是一个暴力些的做法:

    设整个数列分成T段。

    先统计好第i段到第j段每个数出现的个数cnt[i][j][col]和众数f[i][j],时空复杂度为O(nT2)。

    对于每个询问,朴素扫描两端的每一个数,累加到cnt[i][j][col]中,同时更新答案,最后再扫一遍还原就好。

    m次询问,所以时间复杂度为O(mn/T)。

    所以总时间复杂度为O(nT2+mn/T),空间复杂度为O(nT2)。

    根据均值不等式,可以知道T≈3√n时,整个算法复杂度最低,为O(n5/3),可以接受。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline 
    using namespace std;
    
    IL int gi () {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=40010;
    const int INF=0x3f3f3f3f;
    
    int f[41][41],dir[41][41];
    int n,m,Num,tot,len,ans,fa[N],typ[N],mp[N],col[N],a[N],blo[N],L[41],R[41],cnt[41][41][N];
    
    IL int Query (int Ql,int Qr) {
        RG int i,Max=0,mans=INF,Lp=blo[Ql],Rp=blo[Qr];
        if (Rp-Lp<=1) {
            for (i=Ql;i<=Qr;++i) {
                RG int num=++col[mp[i]];
                if (num>Max||(num==Max&&a[i]<mans))
                    Max=num,mans=a[i];
            }
            for (i=Ql;i<=Qr;++i) --col[mp[i]];
        }
        else {
            Max=f[Lp+1][Rp-1],mans=dir[Lp+1][Rp-1];
            for (i=Ql;i<=R[Lp];++i) {
                RG int tt=mp[i];
                RG int num=++cnt[Lp+1][Rp-1][tt];
                if (num>Max||(num==Max&&a[i]<mans))
                    Max=num,mans=a[i];
            }
            for (i=L[Rp];i<=Qr;++i) {
                RG int tt=mp[i];
                RG int num=++cnt[Lp+1][Rp-1][tt];
                if (num>Max||(num==Max&&a[i]<mans))
                    Max=num,mans=a[i];
            }
            for (i=Ql;i<=R[Lp];++i) --cnt[Lp+1][Rp-1][mp[i]];
            for (i=L[Rp];i<=Qr;++i) --cnt[Lp+1][Rp-1][mp[i]];
        }
        return mans;
    }
    
    int main ()
    {
        RG int i,j,k,l,r;
        n=gi(),m=gi();
        Num=(int)pow (n*1.0,1.0/3.0),len=n/Num;
        for (i=1;i<=n;++i) fa[i]=a[i]=gi();
        sort(fa+1,fa+1+n);
        for (i=1;i<=n;++i)
            if (i==1||fa[i]!=fa[i-1]) typ[++tot]=fa[i];
        for (i=1;i<=n;++i) mp[i]=lower_bound (typ+1,typ+1+tot,a[i])-typ;
        for (i=1;i<=Num;++i) {
            L[i]=(i-1)*len+1,R[i]=i*len;
            for (j=L[i];j<=R[i];++j) blo[j]=i;
        }
        if (R[Num]<n) {
            ++Num;
            L[Num]=R[Num-1]+1,R[Num]=n;
            for (i=L[Num];i<=R[Num];++i) blo[i]=Num;
        }
        for (i=1;i<=Num;++i)
            for (j=i;j<=Num;++j) {
                for (k=L[i];k<=R[j];++k) ++cnt[i][j][mp[k]];
                for (k=1;k<=tot;++k)
                    if(f[i][j]<cnt[i][j][k]||(f[i][j]==cnt[i][j][k]&&dir[i][j]>typ[k]))
                        f[i][j]=cnt[i][j][k],dir[i][j]=typ[k];
            }
        while (m--) {
            l=gi(),r=gi();
            l=(l+ans-1)%n+1,r=(r+ans-1)%n+1;
            if (l>r) swap (l,r);
            printf ("%d
    ",ans=Query (l,r));
        }
        return 0;
    }
    BY BHLLX

     三、LuoGu P4135作诗

    一句话题意:在线求区间出现正偶数次的数的个数。

    处理方法:

    预处理出f[i][j]表示第i块到第j块的答案,cnt[i][j]表示前i块中颜色为j的出现的次数(类似前缀和)。

    然后对于每一个询问,能够影响答案的只有两边多余出来的部分。所以只需暴力扫描这些点,利用cnt[][]来算贡献就好了。复杂度O(n√n)。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline 
    using namespace std;
    
    IL int gi () {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=1e5+10;
    const int Sqn=410;
    
    int n,m,c,s,num,ans,len,a[N],C[Sqn][N],f[Sqn][Sqn];
    int blo[N],L[Sqn],R[Sqn],cnt[N],col[N];
    
    IL int query (int l,int r) {
        RG int i,ans=0,tot,res,Lb=blo[l],Rb=blo[r];
        if (Rb-Lb<=1) {
            for (i=l;i<=r;++i) {
                tot=++col[a[i]];
                if (tot==1) continue;
                (tot&1)?--ans:++ans;
            }
            for (i=l;i<=r;++i) --col[a[i]];
        }
        else {
            ans=f[Lb+1][Rb-1];
            for (i=l;i<=R[Lb];++i) {
                tot=++col[a[i]];
                res=tot+C[Rb-1][a[i]]-C[Lb][a[i]];
                if (res==1) continue;
                (res&1)?--ans:++ans;
            }
            for (i=L[Rb];i<=r;++i) {
                tot=++col[a[i]];
                res=tot+C[Rb-1][a[i]]-C[Lb][a[i]];
                if (res==1) continue;
                (res&1)?--ans:++ans;
            }
            for (i=l;i<=R[Lb];++i) --col[a[i]];
            for (i=L[Rb];i<=r;++i) --col[a[i]];
        }
        return ans;
    }
    
    int main ()
    {
        RG int i,j,k,now,l,r;
        n=gi(),c=gi(),m=gi();
        len=sqrt(n)+1,num=n/len+(n%len!=0);
        for (i=1;i<=num;++i) {
            L[i]=R[i-1]+1,R[i]=(i==num)?n:i*len;
            for (j=L[i];j<=R[i];++j) blo[j]=i;
        }
        for (i=1;i<=n;++i) ++C[blo[i]][a[i]=gi()];
        for (i=2;i<=num;++i)
            for (j=1;j<=c;++j)
                C[i][j]+=C[i-1][j];
        for (i=1;i<=num;++i) {
            k=0;
            for (j=L[i];j<=n;++j) {
                now=++cnt[a[j]];
                (now&1)?--k:++k;
                if (now==1) ++k;
                f[i][blo[j]]=k;
            }
            for (j=L[i];j<=n;++j) --cnt[a[j]];
        }
        while (m--) {
            l=gi(),r=gi();
            l=(l+ans)%n+1,r=(r+ans)%n+1;
            if (l>r) l^=r,r^=l,l^=r;
            printf ("%d
    ",ans=query(l,r));
        }
        return 0;
    }
    BY BHLLX

     四、LuoGu P2801 教主的魔法

    描述:

    教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给XMYZ信息组每个英雄看。于是N个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为1、2、……、N。

    每个人的身高一开始都是不超过1000的正整数。教主的魔法每次可以把闭区间[L, R](1≤L≤R≤N)内的英雄的身高全部加上一个整数W。(虽然L=R时并不符合区间的书写规范,但我们可以认为是单独增加第L(R)个英雄的身高)

    CYZ、光哥和ZJQ等人不信教主的邪,于是他们有时候会问WD闭区间 [L, R] 内有多少英雄身高大于等于C,以验证教主的魔法是否真的有效。

    WD巨懒,于是他把这个回答的任务交给了你。

    思路:

    容易想到区间加可以用分块维护,重点是分块后如何统计答案。

    注意到区间加这个操作并不影响 一个完全被包含的块内的各个数之间的大小关系,因此我们考虑给每个块排序。

    对于区间加,只需把两边多余的所在的块重新排序;对于询问,两边多余的暴力统计,中间的块二分查找一下就好了。

    因此可以在O(q√n*log√n)内完成。值得注意的是,由于排了序,所以原位置被打乱,对于两边的统计需要扫描整个两边的块,然后根据位置计算贡献。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline 
    using namespace std;
    
    IL int gi () {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=1e6+10;
    const int sqN=1e3+10;
    
    char opt[2];
    int L[sqN],R[sqN],tag[sqN];
    int n,q,len,num,bel[N];
    
    struct BLOCKS {int id,high;}blo[N];
    IL bool cmp (BLOCKS A,BLOCKS B) {return A.high>B.high;}
    
    IL int search (int id,int x) {
        RG int l=L[id],r=R[id],mid;
        while (l<r) {
            mid=(l+r+1)>>1;
            if (blo[mid].high+tag[id]>=x) l=mid;
            else r=mid-1;
        }
        if (l==L[id]&&blo[l].high+tag[id]<x) return 0;
        return l-L[id]+1;
    }
    
    void modify (int l,int r,int x) {
        RG int i,Lb=bel[l],Rb=bel[r];
        if (Lb==Rb) {
            for (i=L[Lb];i<=R[Lb];++i)
                if (blo[i].id>=l&&blo[i].id<=r) blo[i].high+=x;
            return;
        }
        for (i=L[Lb];i<=R[Lb];++i)
            if (blo[i].id>=l) blo[i].high+=x;
        for (i=L[Rb];i<=R[Rb];++i)
            if (blo[i].id<=r) blo[i].high+=x;
        sort (blo+L[Lb],blo+R[Lb]+1,cmp);
        sort (blo+L[Rb],blo+R[Rb]+1,cmp);
        for (i=Lb+1;i<=Rb-1;++i) tag[i]+=x;
    }
    
    int query (int l,int r,int x) {
        RG int i,Lb=bel[l],Rb=bel[r],ans=0;
        if (Lb==Rb) {
            for (i=L[Lb];i<=R[Lb];++i)
                if (blo[i].id>=l&&blo[i].id<=r) ans+=(blo[i].high+tag[Lb]>=x);
            return ans;
        }
        for (i=L[Lb];i<=R[Lb];++i)
            if (blo[i].id>=l) ans+=(blo[i].high+tag[Lb]>=x);
        for (i=L[Rb];i<=R[Rb];++i)
            if (blo[i].id<=r) ans+=(blo[i].high+tag[Rb]>=x);
        for (i=Lb+1;i<=Rb-1;++i) ans+=search(i,x);
        return ans;
    }
    
    int main ()
    {
        RG int i,l,r,x;
        n=gi(),q=gi(),len=sqrt(n),num=n/len+(n%len!=0);
        for (i=1;i<=n;++i) blo[i].id=i,blo[i].high=gi(),bel[i]=(i-1)/len+1;
        for (i=1;i<=num;++i) L[i]=R[i-1]+1,R[i]=i*len; R[num]=n;
        for (i=1;i<=num;++i) sort(blo+L[i],blo+R[i]+1,cmp);
        while (q--) {
            scanf ("%s",opt+1),l=gi(),r=gi(),x=gi();
            if (opt[1]=='M') modify(l,r,x);
            else printf ("%d
    ",query(l,r,x));
        }
        return 0;
    }
    BY BHLLX
  • 相关阅读:
    win7一关机电脑就蓝屏
    win10系统怎么查自己电脑的IP地址[系统天地]
    Win7如何设置任务管理器的快捷键?[系统天地]
    Win10系统下安全登录密码忘记了怎么办【系统天地】
    Win7系统很卡应该如何解决?[系统天地]
    Win7系统运行慢如何解决?——系统天地
    Win7如何解决开机应用程序无法正常启动
    win10系统怎么切换独立显卡
    程序员的自我修养
    SQL学习笔记
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10125101.html
Copyright © 2020-2023  润新知