zoukankan      html  css  js  c++  java
  • [ZJOI2019]语言(树链剖分+动态开点线段树+启发式合并)

    首先,对于从每个点出发的路径,答案一定是过这个点的路径所覆盖的点数。然后可以做树上差分,对每个点记录路径产生总贡献,然后做一个树剖维护,对每个点维护一个动态开点线段树。最后再从根节点开始做一遍dfs,把每个节点对应的线段树启发式合并即可。时空复杂度均为O(nlog2n)。听说还有一个log的做法,但感觉太神仙不会,不过2个log能过就不管了。

    #include<bits/stdc++.h>
    #define lson l,mid,tr[rt].lc
    #define rson mid+1,r,tr[rt].rc
    using namespace std;
    const int N=1e5+7;
    struct Seg{int lc,rc,tag,sz;}tr[N*300];
    int n,m,cnt,fa[N],sz[N],dep[N],son[N],top[N],dfn[N],rt[N];
    long long ans;
    vector<int>G[N];
    void dfs1(int u,int f)
    {
        sz[u]=1,dep[u]=dep[f]+1,fa[u]=f;
        for(int i=0;i<G[u].size();i++)
        if(G[u][i]!=f)
        {
            dfs1(G[u][i],u),sz[u]+=sz[G[u][i]];
            if(sz[son[u]]<sz[G[u][i]])son[u]=G[u][i];
        }
    }
    void dfs2(int u,int tp)
    {
        top[u]=tp,dfn[u]=++cnt;
        if(son[u])dfs2(son[u],tp);
        for(int i=0;i<G[u].size();i++)if(G[u][i]!=fa[u]&&G[u][i]!=son[u])dfs2(G[u][i],G[u][i]);
    }
    void pushup(int rt,int len)
    {if(tr[rt].tag)tr[rt].sz=len;else tr[rt].sz=tr[tr[rt].lc].sz+tr[tr[rt].rc].sz;}
    void modify(int rt,int v,int len){tr[rt].tag+=v;pushup(rt,len);}
    void update(int L,int R,int v,int l,int r,int&rt)
    {
        if(!rt)rt=++cnt;
        if(L<=l&&r<=R){modify(rt,v,r-l+1);return;}
        int mid=l+r>>1;
        if(L<=mid)update(L,R,v,lson);
        if(R>mid)update(L,R,v,rson);
        pushup(rt,r-l+1);
    }
    void Update(int&rt,int x,int y,int v)
    {
        while(top[x]!=top[y])update(dfn[top[x]],dfn[x],v,1,n,rt),x=fa[top[x]];
        if(x!=y)update(dfn[y]+1,dfn[x],v,1,n,rt);
    }
    int lca(int x,int y)
    {
        while(top[x]!=top[y])if(dep[top[x]]<dep[top[y]])y=fa[top[y]];else x=fa[top[x]];
        return dep[x]<dep[y]?x:y;
    }
    int merge(int u,int v,int l,int r)
    {
        if(!u||!v)return u+v;
        tr[u].tag+=tr[v].tag;
        if(l==r){pushup(u,1);return u;}
        int mid=l+r>>1;
        tr[u].lc=merge(tr[u].lc,tr[v].lc,l,mid);
        tr[u].rc=merge(tr[u].rc,tr[v].rc,mid+1,r);
        pushup(u,r-l+1);
        return u;
    }
    void dfs3(int u,int f)
    {
        for(int i=0;i<G[u].size();i++)
        if(G[u][i]!=f)dfs3(G[u][i],u),rt[u]=merge(rt[u],rt[G[u][i]],1,n);
        ans+=max(tr[rt[u]].sz-1,0);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
        dfs1(1,0);
        dfs2(1,1);
        cnt=0;
        for(int i=1,x,y,f;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            f=lca(x,y);
            Update(rt[x],x,fa[f],1),Update(rt[x],y,f,1);
            Update(rt[y],x,fa[f],1),Update(rt[y],y,f,1);
            Update(rt[f],x,fa[f],-1),Update(rt[f],y,f,-1);
            if(fa[f])Update(rt[fa[f]],x,fa[f],-1),Update(rt[fa[f]],y,f,-1);
        }
        dfs3(1,0);
        cout<<ans/2;
    }
    View Code
  • 相关阅读:
    GCDPlot已提交到Sourceforge,成为开源项目[GCDPlot become a open source software in sourceforge]
    GCDPlot 0.32
    谈谈“科技人员和白领是购买盗版的主力”
    c正则匹配小计
    C++ 模板惯用法
    C++模板常用使用方法介绍
    常用正则表达式
    C++ 容易忘的基本语法和特性
    C++ 对象资源管理惯用法
    Int? 代表什么意思, 可为空的值类型(Nullable<T>)需要注意的地方
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/10816748.html
Copyright © 2011-2022 走看看