• [Ynoi2016]这是我自己的发明 莫队


    传送门:here

    很棒的莫队题啊.....


     

    题意:

    有一棵$ n$个点的树,树上每个点有点权,有$ m$次询问:

    操作1:给定两个点$ x,y$,求二元组$ (a,b)$的数量,要求$ a$在$ x$的子树内,$ b$在$ y$的子树内,且$ a$和$ b$的权值相同

    操作2:给定点$ x$,将根节点换成$ x$


    $ solution:$

    我们先考虑没有换根操作

    我们先求出这棵树所有点的dfs序,然后可以把树上问题转化成区间询问

    $ sumlimits_{i=L1}^{R1}sumlimits_{j=L2}^{R2}[ value[i]=value[j] ]$

    会发现这就是LOJ2254

    我们令$ g(x,L,R)$表示区间$ [L,R]$中x的数量

    则原式等价于$ sumlimits_{x}g(x,L1,R1)g(x,L2,R2)$

    化成前缀相减的形式:$ sumlimits_{x}(g(x,1,R1)-g(x,1,L1-1))(g(x,1,R2)-g(x,L2-1,R2))$

    令$ f(a,b)=sumlimits_{i=1}^{a}sumlimits_{j=1}^{b}[ value[i]=value[j] ]$

    会发现可以化简成$ f(R1,R2)-f(L1-1,R2)-f(R1,L2-1)+f(L1-1,L2-1)$

    对于每个$ f(a,b)$,我们记录它的符号以及在第几个询问里,然后可以直接挂在莫队上跑


     

    然后考虑如果有换根操作

    我们假定原先的根为$ 1$

    然后假设将根换成了$ x$并且询问$ k$的统治区间

    我们分三类讨论

    $ 1.k=x$

    此时子树范围为整个区间

    $ 2.k$在$1...x$的路径上

    显然$ k$是$ x$的祖先

    找出$ k$的孩子中也是$ x$的祖先的节点

    设这个节点在以一号点为根时的子树范围为$ [L,R]$ 

    则此时$ k$的统治范围为$ [1,L-1]并上[R+1,n]$

    $ 3. othercase $

    此时子树和以1为根的子树没有区别

     

    这样我们将每次询问拆成若干区间

    然后用上面没有换根的时候的方法扔进莫队暴力计算

    可以通过此题


     

    附上我的代码(人菜自带大常数)

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define M 200010
    #define ll long long
    using namespace std;
    int i,j,k,m,n,x,y,z,cnt,w;ll now;
    int up[17][M],F[M],L[M],N[M],a[M],fa[M],size[M],deep[M],v[M],dfn[M],to[M];
    int sum[M][2];
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    inline void ins(const int x,const int p){
        now+=sum[x][p^1];
        sum[x][p]++;
    }
    inline void del(const int x,const int p){
        now-=sum[x][p^1];
        sum[x][p]--;
    }
    struct query{
        int L,R,id,zf,p;
        inline bool operator <(const query s)const{
            return (p==s.p)?(R<s.R):(p<s.p);
        }
    }q[4500010];
    void add(int x,int y){
        a[++k]=y;
        if(!F[x])F[x]=k;
        else N[L[x]]=k;
        L[x]=k;
    }
    void dfs(int x,int pre){
        dfn[x]=++cnt;to[cnt]=x;
        up[0][x]=pre;size[x]=1;
        for(int i=F[x];i;i=N[i])if(a[i]!=pre){
            deep[a[i]]=deep[x]+1;
            dfs(a[i],x);
            size[x]+=size[a[i]];
        }
    }
    int son(int x,int y){//求出x的孩子中是y的祖先的那个孩子 
        for(int i=0,s=deep[y]-deep[x]-1;s;i++)if(s>>i&1)s^=(1<<i),y=up[i][y];
        return y;
    }
    bool in(int x,int y){//判断x是否为y的祖先 
        return dfn[x]<=dfn[y]&&dfn[x]+size[x]-1>=dfn[y];
    }
    void add(int id,int L1,int R1,int L2,int R2){//将其加入莫队询问 
        q[++cnt]={R1,R2,id,1,R1/w};
        if(L1>1)q[++cnt]={L1-1,R2,id,-1,(L1-1)/w};
        if(L2>1)q[++cnt]={R1,L2-1,id,-1,R1/w};
        if(L1>1&&L2>1)q[++cnt]={L1-1,L2-1,id,1,(L1-1)/w};    
    }
    struct now{
        int L,R;
    }aa[3],bb[3];
    ll ans[500010];
    struct w{
        int x,id;
        inline bool operator <(const w s)const{
            return x<s.x;
        }
    }c[100010];
    int main(){
        n=read();m=read();w=300;
        for(int i=1;i<=n;i++)c[i].x=read(),c[i].id=i;
        sort(c+1,c+n+1);int vv=0;
        for(int i=1;i<=n;i++){
            if(i==1||c[i].x!=c[i-1].x)vv++;
            v[c[i].id]=vv;
        }//离散化 
        for(int i=1;i<n;i++){
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        dfs(1,1);cnt=0;
        for(int i=1;i<=16;i++)
        for(int j=1;j<=n;j++)
        up[i][j]=up[i-1][up[i-1][j]];
        int nowroot=1,tot=0;
        for(int i=1;i<=m;i++){
            int opt=read();
            if(opt==1){
                nowroot=read();
                continue;
            }
            x=read();y=read();tot++;
            int cnt1=0,cnt2=0;
            if(x==nowroot)aa[++cnt1]={1,n};
            else if(in(x,nowroot)){
                int pl=son(x,nowroot);
                aa[++cnt1]={1,dfn[pl]-1};
                aa[++cnt1]={dfn[pl]+size[pl],n};
            }
            else aa[++cnt1]={dfn[x],dfn[x]+size[x]-1};
            
            if(y==nowroot)bb[++cnt2]={1,n};
            else if(in(y,nowroot)){
                int pl=son(y,nowroot);
                bb[++cnt2]={1,dfn[pl]-1};
                bb[++cnt2]={dfn[pl]+size[pl],n};
            }
            else bb[++cnt2]={dfn[y],dfn[y]+size[y]-1};
            //aa和bb存询问点x,y对应的区间 
            ll ans=0;
            for(int q1=1;q1<=cnt1;q1++)
            for(int q2=1;q2<=cnt2;q2++)
            add(tot,aa[q1].L,aa[q1].R,bb[q2].L,bb[q2].R);//加入询问 
        }
        sort(q+1,q+cnt+1);
        int L=0,R=0;now=0;
        for(int i=1;i<=cnt;i++){
            while(L<q[i].L)ins(v[to[++L]],0);
            while(L>q[i].L)del(v[to[L--]],0);
            while(R<q[i].R)ins(v[to[++R]],1);
            while(R>q[i].R)del(v[to[R--]],1);
            ans[q[i].id]+=now*q[i].zf;//莫队计算 
        }
        for(int i=1;i<=tot;i++)writeln(ans[i]);
        return 0;
    }

     

  • 相关阅读:
    2031 HDOJ 进制转换
    计算机视觉实验之直方图均衡化和对数变换
    sublime text3创建文件时生成头部注释
    浏览器调试出错
    css世界-读书笔记
    redis中Bitmaps位图应用场景
    redis中hyperloglog基数统计
    redis中Zset有序集合类型常用命令
    redis中set集合类型常用命令
    redis中hash哈希类型常用命令
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/9724089.html
Copyright © 2020-2023  润新知