• 莫队


            莫队是一个优美的暴力

       当我们可以在 O(1) 时间推到 L[i-1], L[i+1] , R[i-1] , R[i+1] 的时候 ,就可以使用莫队来解决这个问题啦

      

      一  普通莫队

      首先分块,将询问的区间按照左端点所处的块来排序,然后按顺序处理每一个询问就好了

      这里有一个小 trick ,排序的时候可以按照块的奇偶性来排序,可以大大的提高效率,虽然我并不知道为什么。

      

      经典例题: 小Z的袜子

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 100010;
    
    int n,m,blo;
    int l=1,r=0;
    int c[maxn],cnt[maxn],pos[maxn];
    ll res=0;
    
    struct Q{
        int l,r,id;
    }q[maxn];
    struct Ans{
        ll a,b;
    }ans[maxn];
    
    bool cmp(Q a,Q b){
        if(pos[a.l]==pos[b.l]) return a.r<b.r;
        return a.l<b.l;
    }
    
    void add(int i){
        res-=1ll*cnt[c[i]]*cnt[c[i]];
        ++cnt[c[i]];
        res+=1ll*cnt[c[i]]*cnt[c[i]];
    }
    
    void del(int i){
        res-=1ll*cnt[c[i]]*cnt[c[i]];
        --cnt[c[i]];
        res+=1ll*cnt[c[i]]*cnt[c[i]];
    }
    
    ll gcd(ll a,ll b){
        if(a<b) swap(a,b); 
        return b==0?a:gcd(b,a%b);
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
        n=read(),m=read();
        blo=sqrt(n);
        for(int i=1;i<=n;i++) c[i]=read(),pos[i]=(i-1)/blo+1;
        for(int i=1;i<=m;i++){
            q[i].l=read(),q[i].r=read();
            q[i].id=i;
        }
        
        sort(q+1,q+1+m,cmp); 
        
        for(int i=1;i<=m;i++){
            while(l<q[i].l){ del(l); l++; }
            while(l>q[i].l){ add(l-1); l--; }
            while(r<q[i].r){ add(r+1); r++; }
            while(r>q[i].r){ del(r); r--; }
            
            if(q[i].l==q[i].r){
                ans[q[i].id]=(Ans){0,1};
                continue;
            }
            ans[q[i].id]=(Ans){res-(r-l+1),1ll*(r-l+1)*(r-l)};
        }
        
        for(int i=1;i<=m;i++){
            ll g=gcd(ans[i].a,ans[i].b);
            printf("%lld/%lld\n",ans[i].a/g,ans[i].b/g); 
        }
        
        return 0;
    }
    View Code

      二  带修改莫队

      带修改莫队是在普通莫队的基础上增加了修改操作

      为保证正确性,在询问之前,所有在该次询问时间点之前的修改操作都要修改完 ,之后的修改操作都不能修改

           所以带修莫队要比普通莫队的询问多维护一个时间节点

      那么怎么维护答案呢?

      每次修改答案的时候,如果当前修改的时间点(head)大于询问时间点(q[i].t)那么就将修改还原, 否则继续修改

      剩下的就与普通莫队一样了

      经典例题: 数颜色

      

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 100010;
    
    int n,m,blo,cnt1,cnt2;
    int c[maxn],cnt[1000010],pos[maxn],last[maxn],ans[maxn];
    int L=1,R=0,res,head=0;
    
    struct C{
        int x,y,last;
    }md[maxn];
    struct Q{
        int l,r,id,t;
    }q[maxn];
    bool cmp(Q a,Q b){
        if(pos[a.l]==pos[b.l]){
            if(pos[a.r]==pos[b.r]) return a.t<b.t;
            return pos[a.r]<pos[b.r]; 
        }
        return pos[a.l]<pos[b.l];
    }
    
    void change(int x,int col){
        if(L<=x&&x<=R){
            cnt[c[x]]--; if(cnt[c[x]]==0) res--;
            c[x]=col;
            if(cnt[c[x]]==0) res++; cnt[c[x]]++;
        }else
            c[x]=col;
    }
    
    void add(int x){
        if(cnt[c[x]]==0) ++res;
        ++cnt[c[x]];
    }
    
    void del(int x){
        --cnt[c[x]];
        if(cnt[c[x]]==0) --res;
    }
    inline int getb(int x){ return (x-1)/blo+1; }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
        n=read(),m=read(); blo=sqrt(n)*10;
        for(int i=1;i<=n;i++) c[i]=read(),pos[i]=(i-1)/blo+1,last[i]=c[i];
        
        char op[10];
        for(int i=1;i<=m;i++){
            scanf("%s",op);
            if(op[0]=='R'){
                md[++cnt1].x=read(); md[cnt1].y=read();
                md[cnt1].last=last[md[cnt1].x];
                last[md[cnt1].x]=md[cnt1].y;
            }else{
                q[++cnt2].l=read(),q[cnt2].r=read(),q[cnt2].id=cnt2;
                q[cnt2].t=cnt1;
            }
        }
        
        sort(q+1,q+1+cnt2,cmp);
        
        for(int i=1;i<=cnt2;i++){
            while(head>q[i].t){
                change(md[head].x,md[head].last);
                head--;
            }
            while(head<q[i].t){
                head++;
                change(md[head].x,md[head].y);
            }
            while(L<q[i].l){ del(L); L++; }
            while(L>q[i].l){ add(L-1); L--; }
            while(R<q[i].r){ add(R+1); R++; }
            while(R>q[i].r){ del(R); R--; }
            ans[q[i].id]=res;
        } 
        
        for(int i=1;i<=cnt2;i++) printf("%d\n",ans[i]);
        
        return 0;
    }
    View Code

      三  树上莫队

       树上的莫队当然还是分块啦

      怎么在树上分块呢?  参考一下王室联邦的树分块

      分块代码:

    ·  

    int dfs(int u,int par){ // 树分块 
        int co=1;
        sta[++top]=u;
        dfn[u]=++tot;
        fa[u][0]=par;
        dep[u]=dep[par]+1;
        
        for(int i=1;i<=20;i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(v==par) continue;
            co+=dfs(v,u);
            if(co>=blo){
                ++cnt;
                while(co){
                    kuai[sta[top--]]=cnt;
                    co--;
                }
                co=0;
            }
        }
        return co;
    }

      

      然后就是将树上问题转变成序列问题 -- dfs 序

      接下来的套路与普通的带修改莫队就是一样的了

      每次对答案的维护直接暴力就好  

    void change(int x,int y){  // 暴力更新答案 
        while(x!=y){
            if(dep[x]>dep[y]) update(x),x=fa[x][0];
            else update(y),y=fa[y][0];
        }
    }

      

      要注意处理 lca 的答案

      做完了~

      

    // luogu-judger-enable-o2
    // 糖果公园
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 100010;
    
    int n,m,q,blo,tot,cnt1,cnt2;
    int v[maxn],w[maxn],c[maxn],last[maxn],kuai[maxn],vis[maxn],ci[maxn];
    int head=0;
    ll res=0,ans[maxn];
    
    int h[maxn],size;
    struct E{
        int to,next;
    }e[maxn<<1];
    void add(int u,int v){
        e[++size].to=v;
        e[size].next=h[u];
        h[u]=size;
    }
    
    struct C{
        int x,y,last;
    }rc[maxn];
    struct Q{
        int x,y,bx,by,tim,id;
    }rq[maxn];
    
    bool cmp(Q a,Q b){
        if(a.bx==b.bx){
            if(a.by==b.by) return a.tim<b.tim;
            return a.by<b.by;
        }
        return a.bx<b.bx;
    }
    
    int sta[maxn],top;
    int dep[maxn],fa[maxn][25],dfn[maxn],cnt; // cnt 块的数量  dfn 括号序列 
    
    int dfs(int u,int par){ // 树分块 
        int co=1;
        sta[++top]=u;
        dfn[u]=++tot;
        fa[u][0]=par;
        dep[u]=dep[par]+1;
        
        for(int i=1;i<=20;i++){
            fa[u][i]=fa[fa[u][i-1]][i-1];
        }
        
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(v==par) continue;
            co+=dfs(v,u);
            if(co>=blo){
                ++cnt;
                while(co){
                    kuai[sta[top--]]=cnt;
                    co--;
                }
                co=0;
            }
        }
        
        return co;
    }
    
    int LCA(int u,int v){  // 树上莫队要注意对 LCA 的处理 
        if(dep[u]<dep[v]) swap(u,v);
        for(int j=20;j>=0;j--){
            if(dep[fa[u][j]]>=dep[v]) u=fa[u][j];
        }  if(u==v) return u;
        for(int j=20;j>=0;j--){
            if(fa[u][j]!=fa[v][j]){
                u=fa[u][j],v=fa[v][j];
            }
        }return fa[u][0];
    } 
    
    void update(int x){  // 从答案中消除这个点 
        if(vis[x]){ 
            vis[x]=0; // 标为没有计入答案 
            res-=1ll*w[ci[c[x]]]*v[c[x]];
            --ci[c[x]];
        }else{
            vis[x]=1;
            ++ci[c[x]];
            res+=1ll*w[ci[c[x]]]*v[c[x]];
        }
    }
    
    void change(int x,int y){  // 暴力更新答案 
        while(x!=y){
            if(dep[x]>dep[y]) update(x),x=fa[x][0];
            else update(y),y=fa[y][0];
        }
    }
    
    void modify(int x,int C){
        if(!vis[x]) c[x]=C;
        else{
            update(x);
            c[x]=C;
            update(x);
        } 
    }
    
    int main(){
        memset(h,-1,sizeof(h));
        scanf("%d%d%d",&n,&m,&q);
        blo=(int)pow(n,0.60);
    //    printf("%d\n",blo);
        for(int i=1;i<=m;i++) scanf("%d",&v[i]);
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        int u,v;
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            add(u,v),add(v,u);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&c[i]);
            last[i]=c[i];
        }
        dfs(1,0);
        
        int op,x,y;
        for(int i=1;i<=q;i++){
            scanf("%d%d%d",&op,&x,&y);
            if(op==0){
                rc[++cnt1].x=x,rc[cnt1].y=y,rc[cnt1].last=last[x],last[x]=y;
            }else{
                if(dfn[x]>dfn[y]) swap(x,y); // 括号序列中的顺序 
                rq[++cnt2].x=x,rq[cnt2].y=y,rq[cnt2].tim=cnt1,rq[cnt2].id=cnt2;
                rq[cnt2].bx=kuai[x],rq[cnt2].by=kuai[y];
            }
        }
        
        sort(rq+1,rq+1+cnt2,cmp);
        
        int lca=LCA(rq[1].x,rq[1].y);
        while(head<rq[1].tim) ++head,modify(rc[head].x,rc[head].y);
        change(rq[1].x,rq[1].y);
        update(lca);
        ans[rq[1].id]=res;
        update(lca);
        for(int i=2;i<=cnt2;i++){
            while(head>rq[i].tim) modify(rc[head].x,rc[head].last),--head;
            while(head<rq[i].tim) ++head,modify(rc[head].x,rc[head].y);
            change(rq[i-1].x,rq[i].x);
            change(rq[i-1].y,rq[i].y);
            lca=LCA(rq[i].x,rq[i].y);
            update(lca);
            ans[rq[i].id]=res;
            update(lca); 
        }
        
        for(int i=1;i<=cnt2;i++){
            printf("%lld\n",ans[i]);
        }printf("\n");
        
        return 0;
    }
    View Code
  • 相关阅读:
    《java程序设计》201671010143 周结(11)
    《java程序设计》周结(10)
    《java程序设计》(9)
    201671010142 <<面向对象程序设计(Java) 实验十五 线程 感悟和总结>>
    201671010142 2017-2 《java第十二十三章学习感悟》
    201671010142 2017-2 《java第十二章学习感悟》
    201671010142 2017-2 《java第十一章学习感悟》
    201671010142 2017-2 《java第十章学习感悟》
    201671010142 2017-2 《java第九章学习感悟》
    201671010142 2017-2 《java第八章学习感悟》
  • 原文地址:https://www.cnblogs.com/tuchen/p/10123652.html
Copyright © 2020-2023  润新知