zoukankan      html  css  js  c++  java
  • 「ZJOI2019」语言 解题报告

    「ZJOI2019」语言

    3个(log)做法比较简单,但是写起来还是有点麻烦的。

    大概就是树剖把链划分为(log)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了。

    好像这个可以被优化到两个(log),算了,估计挺麻烦的。


    一个(log)的做法看起来还挺厉害的。

    考虑钦定某个点算它的贡献,于是我们要算的是所有经过它的链的并的大小。

    但是染色这个东西看起来就很不可搞,我们可以挖掘一下这个并的简单性质。

    注意到,这个并是联通的,可以看做是一个生成子树,然后我们需要求这个子树的大小。

    考虑一个类似建虚树的过程,我们把(n)个链拆成(2n-1)条链,即按(dfs)序进行排序,相邻两点构成链,我们按(dfs)序新加入一个点(x)时,新的链的贡献就是(dep_x-dep_{lca(x,y)})(y)是上一个加入的点,这个可以画图体会一下。

    但是一个一个加还是不太好维护,考虑我们可不可以合并两个子树的集合,显然,贡献和左边的最右端(x)以及右边的最左端(y)有关

    然后我们发现需要讨论(x,y)是否在同一条链上,不在就很简单,两棵树没有交,随便算算就可以了。

    如果在的话,我们发现有一部分是重叠的,算出这部分重叠可能需要求出左边的最浅点,为了方便,我们钦定每个集合的最浅点都是根(并加入根),然后在合并的时候可以发现,只需要减去(dep_{lca(x,y)})就可以了,这对上面不在同一条链上仍然是适用的。

    在合并到最后为了去掉根的贡献,我们维护一个点集的(lca)的深度,最后减去它就可以了。

    这样的话,我们就可以用线段树维护这个子树的大小,并支持单点修改与查询。

    对于链的修改,我们可以打差分tag,在相应的节点的线段树中加入与删除链的两个端点(s,t)

    可以发现,为了继承儿子的信息,还需要一个线段树合并。


    Code:

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    #define ll long long
    const int SIZE=1<<21;
    char ibuf[SIZE],*iS,*iT;
    #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
    template <class T>
    void read(T &x)
    {
        x=0;char c=gc();
        while(!isdigit(c)) c=gc();
        while(isdigit(c)) x=x*10+c-'0',c=gc(); 
    }
    const int N=1e5+10;
    int head[N],to[N<<1],Next[N<<1],cnt;
    void add(int u,int v)
    {
        to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
    }
    int n,m;
    ll ans;
    int dfn[N],ha[N],st[N<<1][20],Log[N<<1],fir[N],dep[N],par[N],dfsclock,dcnt;
    void dfs(int now,int fa)
    {
        dfn[now]=++dfsclock;
        ha[dfsclock]=now;
        st[++dcnt][0]=now;
        fir[now]=dcnt;
        dep[now]=dep[fa]+1;
        par[now]=fa;
        for(int v,i=head[now];i;i=Next[i])
            if((v=to[i])!=fa)
            {
                dfs(v,now);
                st[++dcnt][0]=now;
            }
    }
    int LCA(int x,int y)
    {
        if(!x||!y) return 0;
        x=fir[x],y=fir[y];
        if(x>y) std::swap(x,y);
        int d=Log[y+1-x];
        return dep[st[x][d]]<dep[st[y-(1<<d)+1][d]]?st[x][d]:st[y-(1<<d)+1][d];
    }
    struct node
    {
        int a,b,d;
        node(){}
        node(int A,int B,int D){a=A,b=B,d=D;}
    };
    std::vector <node> yuu[N];
    int ch[N*50][2],pot[N*50],lca[N*50],L[N*50],R[N*50],root[N],tot;
    ll sum[N*50];
    #define ls ch[now][0]
    #define rs ch[now][1]
    void updata(int now)
    {
        sum[now]=sum[ls]+sum[rs]-dep[LCA(L[rs],R[ls])];
        L[now]=L[ls]?L[ls]:L[rs],R[now]=R[rs]?R[rs]:R[ls];
        if(lca[ls]&&lca[rs]) lca[now]=LCA(lca[ls],lca[rs]);
        else lca[now]=lca[ls]^lca[rs];	
    }
    void ins(int &now,int l,int r,int p,int d)
    {
        if(!now) now=++tot;
        if(l==r)
        {
            pot[now]+=d;
            if(pot[now]) L[now]=R[now]=lca[now]=ha[l],sum[now]=dep[ha[l]];
            else L[now]=R[now]=lca[now]=sum[now]=0;
            return;
        }
        int mid=l+r>>1;
        if(p<=mid) ins(ls,l,mid,p,d);
        else ins(rs,mid+1,r,p,d);
        updata(now);
    }
    int Merge(int now,int las,int l,int r)
    {
        if(!now||!las) return now^las;
        if(l==r)
        {
            pot[now]+=pot[las];
            if(pot[now]) L[now]=R[now]=lca[now]=ha[l],sum[now]=dep[ha[l]];
            else L[now]=R[now]=lca[now]=sum[now]=0;
            return now;
        }
        int mid=l+r>>1;
        ls=Merge(ls,ch[las][0],l,mid);
        rs=Merge(rs,ch[las][1],mid+1,r);
        updata(now);
        return now;
    }
    void dfs(int now)
    {
        for(int v,i=head[now];i;i=Next[i])
            if((v=to[i])!=par[now])
            {
                dfs(v);
                root[now]=Merge(root[now],root[v],1,n);
            }
        for(int i=0;i<yuu[now].size();i++)
        {
            int a=yuu[now][i].a,b=yuu[now][i].b,d=yuu[now][i].d;
            ins(root[now],1,n,dfn[a],d);
            ins(root[now],1,n,dfn[b],d);
        }
        //ll lastans=ans;
        ans+=sum[root[now]]-dep[lca[root[now]]];
        //printf("%d %lld
    ",now,ans-lastans);
    }
    int main()
    {
        read(n),read(m);
        for(int u,v,i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
        dfs(1,0);
        for(int i=2;i<=dcnt;i++) Log[i]=Log[i>>1]+1;
        for(int j=1;j<=18;j++)
        {
            for(int i=1;i<=dcnt-(1<<j)+1;i++)
            {
                int x=st[i][j-1],y=st[i+(1<<j-1)][j-1];
                st[i][j]=dep[x]<dep[y]?x:y;
            }
        }
        for(int s,t,lca,i=1;i<=m;i++)
        {
            read(s),read(t);
            yuu[s].push_back(node(s,t,1));
            yuu[t].push_back(node(s,t,1));
            lca=LCA(s,t);
            yuu[lca].push_back(node(s,t,-1));
            yuu[par[lca]].push_back(node(s,t,-1));
        }
        dfs(1);
        printf("%lld
    ",ans>>1);
        return 0;
    }
    

    2019.5.9

  • 相关阅读:
    排序算法系列之冒泡排序 (3)
    排序算法系列之选择排序 (2)
    排序算法系列之插入排序 (1)
    深挖 NGUI 基础 之UICamera (二)
    深挖 NGUI 基础 之UIRoot (一)
    JPS寻路和AStar寻路
    旋转矩阵
    [斜边的血条进度]
    UI框架:ui节点控件的绑定方式
    Shader播放序列帧
  • 原文地址:https://www.cnblogs.com/butterflydew/p/10840896.html
Copyright © 2011-2022 走看看