• 莫队


    一、LuoGu P3709 大爷的字符串

    描述:

    给你一个字符串a,每次询问一段区间的贡献

    贡献定义:每次从这个区间中随机拿出一个字符x,然后把x从这个区间中删除,你要维护一个集合S

    如果S为空,你rp减1

    如果S中有一个元素不小于x,则你rp减1,清空S

    之后将x插入S

    由于你是大爷,平时做过的题考试都会考到,所以每次询问你搞完这段区间的字符之后最多还有多少rp?rp初始为0

    询问之间不互相影响。

    吐槽:这个题题面十分恶心,看了半天没搞清要干什么,瞟了一下题解发现就是求区间众数,真看语文水平。

    思路:

    既然是区间众数,那么不就是直接按蒲公英的算法来?

    的确可以,只是既然这个题都没有强制在线,我们是不是可以考虑发挥一下?

    区间的话考虑莫队。比较套路的,我们可以记录下当前区间内数的出现次数,很容易维护,主要考虑如何统计答案。

    这里要用到一个莫队中可能常需要用到的统计答案的方法:对答案分块。可以发现,分块统计答案仍然保证了复杂度的正确性。

    在此题中,我们对次数分块,并对于每一个次数 都开一个数组记录 有多少个数出现了这么多次,然后对于每一次移动,相对应的修改次数的数组值就好了。

    ps:事实证明我写莫队还是作死了一点,卡了好一会常才过,最慢的一个点999ms。

    #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=2e5+10;
    
    int n,m,len,num,tot,a[N],b[N],c[N],blo[N],cnt[N],ans[N];
    
    struct query {int id,l,r;}qur[N];
    IL bool cmp (query A,query B) {return blo[A.l]==blo[B.l]?A.r<B.r:A.l<B.l;}
    
    struct BLOCKS {int l,r,cnt,s[510];}B[510];
    
    IL void modify (int pos,int v) {
        RG int cnum=cnt[a[pos]],bel=blo[cnum];
        if (cnum&&(--B[bel].s[cnum-B[bel].l+1]==0)) --B[bel].cnt;
        cnum=(cnt[a[pos]]+=v),bel=blo[cnum];
        if (cnum&&(++B[bel].s[cnum-B[bel].l+1]==1)) ++B[bel].cnt;
    }
    
    IL int Query () {
        RG int i,dir;
        for (i=num;i>=1;--i)
            if (B[i].cnt) {dir=i;break;};
        for (i=B[i].r;i;--i)
            if (B[dir].s[i]) return B[dir].l+i-1;
    }
    
    int main ()
    {
        RG int i,x,y,Ldir=1,Rdir=0;
        n=gi(),m=gi(),len=sqrt(n),num=n/len+(n%len!=0);
        for (i=1;i<=n;++i) b[i]=a[i]=gi(),blo[i]=(i-1)/len+1;
        sort (b+1,b+n+1);
        for (i=1;i<=n;++i) if (b[i]!=b[i-1]) c[++tot]=b[i];
        for (i=1;i<=n;++i) a[i]=lower_bound(c+1,c+tot+1,a[i])-c;
        for (i=1;i<num;++i) B[i].l=(i-1)*len+1,B[i].r=len;
        B[num].l=(num-1)*len+1,B[num].r=n-B[num].l+1;
        for (i=1;i<=m;++i) qur[i].id=i,qur[i].l=gi(),qur[i].r=gi();
        sort (qur+1,qur+m+1,cmp);
        for (i=1;i<=m;++i) {
            while (Ldir>qur[i].l) modify(--Ldir,1);
            while (Ldir<qur[i].l) modify(Ldir++,-1);
            while (Rdir>qur[i].r) modify(Rdir--,-1);
            while (Rdir<qur[i].r) modify(++Rdir,1);
            ans[qur[i].id]=Query();
        }
        for (i=1;i<=m;++i) printf ("%d
    ",-ans[i]);
        return 0;
    }
    BY BHLLX

     二、LuoGu P3674 小清新人渣的本愿

    描述:

    给你一个序列a,长度为n,有m次操作,每次询问一个区间是否可以选出两个数它们的差为x,或者询问一个区间是否可以选出两个数它们的和为x,或者询问一个区间是否可以选出两个数它们的乘积为x ,这三个操作分别为操作1,2,3

    选出的这两个数可以是同一个位置的数。

    思路:

    对于1操作:我们可以维护一个数组now,记录每个数是否在当前区间中出现,对于询问只要考虑把这个01数组整体左移x位,和原数组与(&)一下, 至少有一位上为1就说明满足。

    可是这样的话复杂度是线性的,所以我们需要用bitset优化。

    对于2操作:考虑和上面的一样,但比较灵活一点。我们考虑如何构造:假设存在a+b=x,那么存在(N-a)-(N-x)=b。所以我们考虑另外维护一个bitset记录(N-这个数)是否出现,记为fnow。

    那么对于询问,只需把fnow右移(N-x)位,再和now与(&)一下就好了。

    对于3操作:乍一看不能和上面一样维护,但是考虑是乘积的形式,所以O(√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;
    
    bitset<N+10> now,fnow;
    int n,m,len,a[N+10],cnt[N+10],ans[N+10];
    
    struct query {int id,typ,l,r,v,bel;}qur[N+10];
    IL bool cmp (query A,query B) {return A.bel==B.bel?A.r<B.r:A.l<B.l;}
    
    IL void add (int col) {if (++cnt[col]==1) now[col]=fnow[N-col]=1;}
    IL void del (int col) {if (--cnt[col]==0) now[col]=fnow[N-col]=0;}
    
    int main ()
    {
        RG int i,j,ty,x,y,v,Ldir=1,Rdir=0;
        n=gi(),m=gi(),len=sqrt(n);
        for (i=1;i<=n;++i) a[i]=gi();
        for (i=1;i<=m;++i)
            ty=gi(),x=gi(),y=gi(),v=gi(),qur[i]=(query){i,ty,x,y,v,(x-1)/len+1};
        sort (qur+1,qur+m+1,cmp);
        for (i=1;i<=m;++i) {
            while (Ldir>qur[i].l) add(a[--Ldir]);
            while (Ldir<qur[i].l) del(a[Ldir++]);
            while (Rdir>qur[i].r) del(a[Rdir--]);
            while (Rdir<qur[i].r) add(a[++Rdir]);
            if (qur[i].typ==1) if ((now&(now<<qur[i].v)).any()) ans[qur[i].id]=1;     
            if (qur[i].typ==2) if ((now&(fnow>>(N-qur[i].v))).any()) ans[qur[i].id]=1;
            if (qur[i].typ==3) {
                for (j=1;j*j<=qur[i].v;++j)
                    if (qur[i].v%j==0&&now[j]&&now[qur[i].v/j]) {ans[qur[i].id]=1;break;} 
            }
        }
        for (i=1;i<=m;++i) puts(ans[i]?"hana":"bi");
        return 0;
    }
    BY BHLLX

     WC2013 糖果公园 

    描述:懒得蒯了   LuoGuP4074

    思路:

    树上待修莫队板子题。

    和普通待修莫队不一样的地方:

    1、询问分块方式。一般有两种分块方式:一种是dfs一遍,用栈记录,辅助分块。另一种是利用dfs序来分块。我比较喜欢前者。

    2、统计答案时需要注意一些东西。假设:原本是a,b两点,且对于原来的有a,b间路径除LCA(a,b)外其他点都被标记为经过,现在是c,d两点。

    主要讨论一下LCA的问题:

    1°、如果LCA(a,b)还在c,d的路径上,那么必然至少一个点还在以LCA(a,b)为根的子树中,所以只会有至多一个点经过LCA(a,b),标记为经过。

      而LCA(c,d)不会有点经过,此时间c,d路径除LCA(c,d)外其他点都被标记为经过,符合假设。

    2°、如果LCA(a,b)不在c,d的路径上,那么两个点都会经过LCA(a,b),相当于没操作依然没被标记,不影响,符合假设。

    综上可知,我们每次移动后需对LCA处单独处理一下,统计完答案后还需还原。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define LL long long 
    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;
    
    LL Ans,ans[N];
    int top,sta[N];
    int tot,head[N];
    int Tim,dfn[N],dep[N],f[N][21];
    int n,m,q,qnum,a[N],V[N],W[N],C[N];
    int len,bnum,md[N][2],blo[N],vis[N],cnt[N];
    
    struct Edge {int next,to;}e[N<<1];
    IL void make (int from,int to) {
        e[++tot]=(Edge){head[from],to};
        head[from]=tot;
    }
    
    struct Query {int id,u,v,T;}qur[N];
    IL bool cmp (Query A,Query B) {
        if (blo[A.u]==blo[B.u])
            return (blo[A.v]==blo[B.v])?A.T<B.T:dfn[A.v]<dfn[B.v];
        return dfn[A.u]<dfn[B.u];
    }
    
    void dfs (int x,int fx) {
        RG int i,y,tp=top;
        f[x][0]=fx,dep[x]=dep[fx]+1,dfn[x]=++Tim;
        for (i=head[x];i;i=e[i].next) {
            if ((y=e[i].to)==fx) continue;
            dfs (y,x);
            if (top-tp>len) {
                ++bnum;
                while (top>tp) blo[sta[top--]]=bnum;
            }
        }
        sta[++top]=x;
    }
    
    IL void get_f () {
        for (RG int i=1;i<=20;++i)
            for (RG int j=1;j<=n;++j)
                f[j][i]=f[f[j][i-1]][i-1];
    }
    
    IL int get_lca (int x,int y) {
        RG int i;
        if (dep[x]<dep[y]) swap(x,y);
        for (i=20;i>=0;--i)
            if (dep[f[x][i]]>=dep[y]) x=f[x][i];
        if (x==y) return x;
        for (i=20;i>=0;--i)
            if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    
    IL void add (int col) {Ans+=(LL)V[col]*W[++cnt[col]];}
    IL void del (int col) {Ans-=(LL)V[col]*W[cnt[col]--];}
    IL void remove (int pos) {(vis[pos]^=1)?add(C[pos]):del(C[pos]);}
    
    IL void modify (int T) {
        if (vis[md[T][0]]) add(md[T][1]),del(C[md[T][0]]);
        swap (md[T][1],C[md[T][0]]);
    }
    
    IL void GO (int x,int y) {
        if (dep[x]<dep[y]) swap (x,y);
        while (dep[x]>dep[y]) remove(x),x=f[x][0];
        while (x!=y) remove(x),remove(y),x=f[x][0],y=f[y][0];
    }
    
    int main ()
    {
        RG int i,op,x,y,T=0,Tdir=0,u=1,v=1,LCA;
        n=gi(),m=gi(),q=gi(),len=pow(n,2.0/3.0);
        for (i=1;i<=m;++i) V[i]=gi();
        for (i=1;i<=n;++i) W[i]=gi();
        for (i=1;i<n;++i) {
            x=gi(),y=gi();
            make (x,y),make (y,x);
        }
        for (i=1;i<=n;++i) C[i]=gi();
        for (i=1;i<=q;++i) {
            op=gi(),x=gi(),y=gi();
            if (op==1) qur[++qnum]=(Query){qnum,x,y,T};
            else md[++T][0]=x,md[T][1]=y;
        }
        dfs (1,1),get_f ();
        while (top) blo[sta[top--]]=bnum+1;++bnum;
        sort (qur+1,qur+qnum+1,cmp);
        for (i=1;i<=qnum;++i) {
            while (Tdir>qur[i].T) modify(Tdir--);
            while (Tdir<qur[i].T) modify(++Tdir);
            while (u!=qur[i].u) GO(u,qur[i].u),u=qur[i].u;
            while (v!=qur[i].v) GO(v,qur[i].v),v=qur[i].v;
            remove(LCA=get_lca(u,v)),ans[qur[i].id]=Ans,remove(LCA);
        }
        for (i=1;i<=qnum;++i) printf ("%lld
    ",ans[i]);
        return 0;
    }
    BY BHLLX
  • 相关阅读:
    sed 简明教程
    简明 Vim 练级攻略
    AWK 简明教程
    TCP 的那些事儿(下)
    TCP 的那些事儿(上)
    CentOS 7系统安装配置图解教程
    Google Chrome谷歌/火狐/Safari浏览器开发者工具基本使用教程
    《Ext JS模板与组件基本框架图----组件》
    ExtJS关于组件Component生命周期
    《Ext JS模板与组件基本知识框架图----模板》
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10161777.html
Copyright © 2020-2023  润新知