Codechef BTREE Union on Tree

▼魔方 西西 提交于 2020-02-15 09:26:58

Link
首先可以很自然地想到把虚树建出来然后在上面搞。
我们做两遍dp,把每个点的\(r_i\)更新成从这个点出来能覆盖的最远距离和从其他点出来经过这个点后能够覆盖的最远距离的最大值。
这样我们保证了对于一条边\((u,v)\)\(u\)\(v\)的父亲),一定存在一个点\(w\)使得\(v\)\(u\)更新\(w\)更优。
那么我们先计算出所有更新后\(U(x_i,r_i)\)能够覆盖到的点的数目。
这样子肯定会算重,我们再考虑把算重的减掉。
算重的部分相当于找到上文说的那个\(w\),计算有多少点在\(w\)上面并且被\(v\)的范围包含,以及在\(w\)下面并且被\(u\)的范围包含。
\(r_u-(dep_w-dep_u)=r_v-(dep_v-dep_w)\)可以确定\(w\)的位置。
同时可以发现\(w\)往上往下延伸的范围是相等的,这就相当于是\(U(w,r_u-(dep_w-dep_u))\)
那么我们现在需要做的就是求\(|U(x,r)|\)
这个可以建出点分树然后暴力跳父亲一层层统计。
注意到\(w\)可能在边上,所以一开始化边为点即可。

#include<cstdio>
#include<cctype>
#include<vector>
#include<numeric>
#include<cstring>
#include<algorithm>
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
}
using IO::read;
const int N=100007;
int n,m;
std::vector<int>e[N];
namespace TreeI
{
    int all,root,mx[N],size[N],vis[N],dep[N],dis[N][20],fa[N][20],cnt[N];
    std::vector<int>vec[N][2];
    void findroot(int u,int fa)
    {
    size[u]=1,mx[u]=0;
    for(int v:e[u]) if(!vis[v]&&v^fa) findroot(v,u),size[u]+=size[v],mx[u]=std::max(mx[u],size[v]);
    mx[u]=std::max(mx[u],all-size[u]),root=mx[u]<mx[root]? u:root;
    }
    void calc(int u,int f,int root,int d)
    {
    cnt[root]+=u<=n,vec[root][0][d]+=u<=n,vec[root][1][dis[u][dep[u]]]+=u<=n,fa[u][++dep[u]]=root,dis[u][dep[u]]=d;
    for(int v:e[u]) if(!vis[v]&&v^f) calc(v,u,root,d+1);
    }
    void solve(int u)
    {
    int tot=all;vis[u]=1,vec[u][0].resize(tot+2),vec[u][1].resize(tot+2),calc(u,0,u,0);
    for(int i=0;i<2;++i) std::partial_sum(vec[u][i].begin(),vec[u][i].end(),vec[u][i].begin());
    for(int v:e[u]) if(!vis[v]) all=size[v]>size[u]? tot-size[u]:size[v],findroot(v,root=0),solve(root);
    }
    int query(int u,int p,int o){return !u? 0:(p>(int)vec[u][o].size()-1? cnt[u]:vec[u][o][p]);}
    int query(int u,int r)
    {
    int s=0;
    if(r<0)return 0;
    for(int i=dep[u];i;--i) if(r>=dis[u][i]) s+=query(fa[u][i],r-dis[u][i],0)-query(fa[u][i+1],r-dis[u][i],1);
    return s;
    }
    void build(){mx[0]=all=2*n-1,findroot(1,root=0),solve(root);}
}
namespace TreeII
{
    int fa[N],dep[N],size[N],son[N],top[N],dfn[N],id[N];
    void dfs1(int u,int f)
    {
    fa[u]=f,dep[u]=dep[f]+1,size[u]=1;
    for(int v:e[u]) if(v^f) dfs1(v,u),size[u]+=size[v],son[u]=size[son[u]]>size[v]? son[u]:v;
    }
    void dfs2(int u,int tp)
    {
    top[u]=tp,id[dfn[u]=++dfn[0]]=u;
    if(son[u]) dfs2(son[u],tp);
    for(int v:e[u]) if(v^fa[u]&&v^son[u]) dfs2(v,v);
    }
    int lca(int u,int v){for(;top[u]^top[v];v=fa[top[v]])if(dep[top[u]]>dep[top[v]])std::swap(u,v);return dep[u]<dep[v]? u:v;}
    int jump(int u,int d){for(;dep[top[u]]>d;u=fa[top[u]]);return id[dfn[u]-dep[u]+d];}
    void build(){dfs1(1,0),dfs2(1,1);}
}
namespace TreeIII
{
    using TreeII::dep;
    int top,cnt,a[N],vis[N],r[N],stk[N],fa[N];
    void dfs(int u)
    {
    a[++cnt]=u;
    if(!vis[u]) r[u]=-1;
    for(int v:e[u]) fa[v]=u,dfs(v);
    e[u].clear();
    }
    void work()
    {
    m=read(),cnt=0;int ans=0;
    for(int i=1;i<=m;++i) a[i]=read(),r[a[i]]=read()*2,vis[a[i]]=1;
    std::sort(a+1,a+m+1,[](int u,int v){return TreeII::dfn[u]<TreeII::dfn[v];}),stk[top=1]=1;
    for(int i=1,u,v;i<=m;++i)
    {
            u=a[i],v=TreeII::lca(u,stk[top]);
            while(dep[v]<dep[stk[top]])
        {
                if(dep[v]>=dep[stk[top-1]])
        {
                    e[v].push_back(stk[top]),--top;
                    if(stk[top]^v) stk[++top]=v;
                    break;
                }
        e[stk[top-1]].push_back(stk[top]),--top;
            }
            if(stk[top]^u) stk[++top]=u;
        }
    for(;top>1;--top) e[stk[top-1]].push_back(stk[top]);
    dfs(1);
        for(int i=cnt;i^1;--i) r[fa[a[i]]]=std::max(r[fa[a[i]]],r[a[i]]-dep[a[i]]+dep[fa[a[i]]]);
    for(int i=2;i<=cnt;++i) r[a[i]]=std::max(r[a[i]],r[fa[a[i]]]-dep[a[i]]+dep[fa[a[i]]]);
    for(int i=1;i<=cnt;++i) ans+=TreeI::query(a[i],r[a[i]]);
    for(int i=2,u,v,w;i<=cnt;++i) u=a[i],v=fa[u],w=TreeII::jump(u,(dep[u]-r[u]+dep[v]+r[v])/2),ans-=TreeI::query(w,r[u]-dep[u]+dep[w]);
    printf("%d\n",ans);
    for(int i=1;i<=cnt;++i) vis[a[i]]=0;
}
}
int main()
{
    n=read();
    for(int i=1,u,v;i<n;++i) u=read(),v=read(),e[u].push_back(i+n),e[v].push_back(i+n),e[i+n].push_back(u),e[i+n].push_back(v);
    TreeI::build(),TreeII::build();
    for(int i=1;i<2*n;++i) e[i].clear();
    for(int q=read();q;--q) TreeIII::work();
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!