【[HEOI2014]大工程 】

可能是虚树板子题了

首先先把虚树建出来,但是这里和那道虚树的入门题不一样,这里所有的询问点都得在虚树里,所以不会存在那种直接不如栈的点

之后我们考虑一下这个三个要求的东西

第一个操作我们需要统计虚树上每一条边的贡献,即被多少个点对经过,根据乘法原理显然有\((t-sz[x])\times sz[x]\times w\)的贡献

第二个操作是最大的距离,这不就是直径吗,子树内部最长链和次长链拼一下就好了

第三个操作是所有点对之间的最小值,因为虚树上有一些点并不是被询问的点,所以不能直接从虚树上选择最小的边,还是需要\(dp\)一下

我们用\(g[x]\)表示\(x\)子树内部的距离\(x\)最近的标记过得点到\(x\)的距离,如果\(x\)被标记了,那么就在这次\(dfs\)之后把其变成\(0\),之后像直径那样合并就好了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define re register
#define LL long long
#define maxn 1000005
#define INF 99999999999
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    re char c=getchar();int x=0;
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int n,m,num,__,t,top;
LL ans,tot,cnt=INF;
int head[maxn],head_[maxn],a[maxn],st[maxn];
int Top[maxn],fa[maxn],sum[maxn],deep[maxn],son[maxn],dfn[maxn];
LL sz[maxn],dp[maxn],f[maxn],to[maxn],g[maxn],to_[maxn];
struct E {int v,nxt;}e[maxn<<1];
struct Eg {int v,nxt,w;}e_[maxn<<1];
inline int cmp(int x,int y){return dfn[x]<dfn[y];}
inline void add_edge(int x,int y){e[++num].v=y,e[num].nxt=head[x],head[x]=num;}
inline void add(int x,int y,int z){e_[++num].v=y,e_[num].nxt=head_[x],e_[num].w=z,head_[x]=num;}
void dfs1(int x)
{
    sum[x]=1;
    int maxx=-1;
    for(re int i=head[x];i;i=e[i].nxt)
    if(!deep[e[i].v])
    {
        deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
        dfs1(e[i].v);
        sum[x]+=sum[e[i].v];
        if(sum[e[i].v]>maxx) maxx=sum[e[i].v],son[x]=e[i].v;
    }
}
void dfs2(int x,int topf)
{
    Top[x]=topf,dfn[x]=++__;
    if(!son[x]) return;
    dfs2(son[x],topf);
    for(re int i=head[x];i;i=e[i].nxt) if(!Top[e[i].v]) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y)
{
    while(Top[x]!=Top[y]){if(deep[Top[x]]<deep[Top[y]]) std::swap(x,y);x=fa[Top[x]];}
    if(deep[x]<deep[y]) return x;return y;
}
inline void ins(int x)
{
    if(top==1){st[++top]=x;return;}
    int lca=LCA(x,st[top]);
    while(top>1&&dfn[st[top-1]]>=dfn[lca])
        add(st[top-1],st[top],deep[st[top]]-deep[st[top-1]]),top--;
    if(lca!=st[top]) add(lca,st[top],deep[st[top]]-deep[lca]),st[top]=lca;
    st[++top]=x;
}
void Dfs(int x)
{
    for(re int i=head_[x];i;i=e_[i].nxt)
    if(deep[e_[i].v]>deep[x])
    {
        Dfs(e_[i].v);
        sz[x]+=sz[e_[i].v];
        tot+=sz[e_[i].v]*((LL)t-sz[e_[i].v])*(LL)e_[i].w;
        if(dp[x]<dp[e_[i].v]+e_[i].w) dp[x]=dp[e_[i].v]+e_[i].w,to[x]=e_[i].v;
        if(g[x]>g[e_[i].v]+e_[i].w) g[x]=g[e_[i].v]+e_[i].w,to_[x]=e_[i].v;
    }
    ans=max(ans,dp[x]);
    if(f[x]) cnt=min(cnt,g[x]);
    for(re int i=head_[x];i;i=e_[i].nxt)
    if(deep[e_[i].v]>deep[x]&&to[x]!=e_[i].v) ans=max(ans,dp[x]+dp[e_[i].v]+(LL)e_[i].w);
    for(re int i=head_[x];i;i=e_[i].nxt)
    if(deep[e_[i].v]>deep[x]&&to_[x]!=e_[i].v) cnt=min(cnt,g[x]+g[e_[i].v]+(LL)e_[i].w);
    if(f[x]) g[x]=0;
}
void clear(int x)
{
    sz[x]=0;dp[x]=0,to[x]=0;f[x]=0;g[x]=INF;to_[x]=0;
    for(re int i=head_[x];i;i=e_[i].nxt)
    if(deep[e_[i].v]>deep[x]) clear(e_[i].v);
    head_[x]=0;
}
int main()
{
    n=read();
    int x,y;
    for(re int i=1;i<n;i++) {x=read(),y=read();add_edge(x,y),add_edge(y,x);g[i]=INF;}g[n]=INF;
    deep[1]=1,dfs1(1),dfs2(1,1);
    m=read();
    while(m--)
    {
        t=read();num=0;ans=0;tot=0;cnt=INF;
        for(re int i=1;i<=t;i++) a[i]=read(),sz[a[i]]++,f[a[i]]=1;
        std::sort(a+1,a+t+1,cmp);top=0;
        int root=LCA(a[1],a[2]);
        for(re int i=3;i<=t;i++) root=LCA(root,a[i]);
        st[++top]=root;
        for(re int i=1;i<=t;i++) if(root!=a[i]) ins(a[i]);
        while(top) add(st[top-1],st[top],deep[st[top]]-deep[st[top-1]]),top--;
        Dfs(root);
        printf("%lld %lld %lld\n",tot,cnt,ans);
        clear(root);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/asuldb/p/10205620.html

时间: 2024-09-28 08:54:18

【[HEOI2014]大工程 】的相关文章

[Bzoj3611][Heoi2014]大工程(虚树)

3611: [Heoi2014]大工程 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 2000  Solved: 837[Submit][Status][Discuss] Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径. 现在国家有很多个计划,每个计划都是这样,

bzoj 3611: [Heoi2014]大工程

Description 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径. 现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道. 现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 Input 第一行 n

[HEOI2014]大工程

题目描述 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径. 现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道.现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 输入输出格式 输入格式: 第一行 n 表

BZOJ3611 [Heoi2014]大工程 【虚树】

题目 国家有一个大工程,要给一个非常大的交通网络里建一些新的通道. 我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上. 在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径. 现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道. 现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少 输入格式 第一行 n 表示点数. 接下来

BZOJ 3611: [Heoi2014]大工程 [虚树 DP]

传送门 题意: 多次询问,求最长链最短链链总长 煞笔$DP$记录$d,c,f,g$ $MD$该死拍了一下午没问题然后交上去就$T$ 然后发现树链剖分写成$size[v]+=size[u]$ 我想知道我随机生成的大数据是怎么跑过去的!!!!!!!! #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using

[bzoj3611][Heoi2014]大工程

看题目感觉应该就是传说中的虚树? 然后跑去学了一发...自己YY了一下然后挂飞..于是就只好抄模板了T_T 建完虚树就是个树形dp... 对于询问总和:每条边对答案的贡献是边权*一端的节点数*另一端的节点数.(这里的节点不包括建虚树时添上去的点) 对于询问最小值最大值,每次计算出经过这个节点的最长||最短路径长度就好了.. 大概这种题条件都有一个sigma(K)<=n之类的..而且题目求的东西得符合区间加法...不然你把边合在一起也没用>_< 链剖求lca果然快...速度能进前10...

BZOJ 3611 HEOI2014 大工程 倍增LCA+单调栈+树形DP

题目大意:给定一棵树,m次询问,每次给出k个关键点,询问这k个点之间的两两距离和.最小距离和最大距离 n<=100W,m<=50000,Σk<=2*n 处理方法同2286 消耗战 地址见 http://blog.csdn.net/popoqqq/article/details/42493725 这个题的DP有些麻烦 因此我把要处理的节点单独拎出来做的DP 具体状态和转移见代码 #include <cstdio> #include <cstring> #includ

虚树(Bzoj3611: [Heoi2014]大工程)

题面 传送门 虚树 把跟询问有关的点拿出来建树,为了方便树\(DP\) 在\(LCA\)处要合并答案,那么把这些点的\(LCA\)也拿出来 做法:把点按\(dfs\)序排列,然后求出相邻两个点的\(LCA\),把这些点建一个虚树,维护一个栈就好了 Sol 虚树+树\(DP\) # include <bits/stdc++.h> # define IL inline # define RG register # define Fill(a, b) memset(a, b, sizeof(a))

LibreOJ #2219. 「HEOI2014」大工程

二次联通门 : LibreOJ #2219. 「HEOI2014」大工程 /* LibreOJ #2219. 「HEOI2014」大工程 虚树 + dp 对于每次的关键点建好虚树后 考虑树形dp dp[i] 表示在以i为根的子树路径总长 size[i] 表示在以i为根的子树中关键点的个数 maxn为以i为根的子树中到关键点到根最长的路径长 ans1自己推一推就好 对于ans2 如果i是关键点,则直接用maxn[i]更新答案 否则用maxn[i]+maxn[child[i]]+dis(i,son[