[COCI 2015] kamp 聚会

来源:互联网 发布:c语言if是什么意思 编辑:程序博客网 时间:2024/05/17 07:00

题目大意:n个点的一棵树上有k个关键点,每条边有一个距离,要求计算从每个点出发,用一条路径按任意顺序分别访问这k个点的路径总长度的最小值。(k<=n<=500000)

让我想艹的细节题。。。。一开始以为是sdoi2015的寻宝游戏,但我不会写那题。。。。。。
后来自已yy了一个O(n)的树形dp,但是细节TMD的多。

首先从1号点跑一遍dp。
用f1[i]表示i号节点的子树的访问关键点的最小代价,g1[i]表示访问完关键点还要回到第i号点的最小代价。然后把那些子树中不包含关键节点的儿子砍掉,可以得出

这里写图片描述

其中dis表示i号节点到他所有有用孩子的距离之和,w[i][son]表示i号点到这个孩子的距离。(即从i号点向下走遍所有有用节点再回来,然后选择一个代价最小的停下)
用_max1[i]保存一下那个f1[i]上取max的那个东西,_max2[i]存一下次大值。

然后再跑一遍dp,计算出答案。
用f2[i]表示i号节点的父亲除了i这个孩子以外的答案,g2[i]表示它的父亲除它之外的走完所有关键点再回到它父亲的最小值,f3[i]表示i号节点的最终答案,g3[i]表示最终答案还要再走回来的值。转移类比一下上面那个,什么减一减,是它最大就选次大之类的(细节太多就不写了,全写出来的一大长串看着也烦 总之就是把它父亲当做它的儿子来做就对了。。。。)

(我语死早求轻喷)

#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <cstdlib>#include <algorithm>#include <vector>#include <deque>#include <queue>#include <set>#include <map>#include <ctime>using namespace std;typedef long long LL;const int MAXN=500005; const LL INF=LL(1)<<LL(60);int n,m,tot,pos[MAXN],fst[MAXN],pre[MAXN*2],to[MAXN*2],cost[MAXN*2];LL f1[MAXN],f2[MAXN],g1[MAXN],g2[MAXN],f3[MAXN],g3[MAXN],_max1[MAXN],_max2[MAXN],fa[MAXN],fadis[MAXN]; bool vis[MAXN],flag[MAXN]; int Get(){    char ch; int v=0; bool f=false;    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;    while (isdigit(ch=getchar())) v=v*10+ch-48;    if (f) return -v;else return v; }void add(int x,int y,int c){    pre[++tot]=fst[x],fst[x]=tot,to[tot]=y,cost[tot]=c;    pre[++tot]=fst[y],fst[y]=tot,to[tot]=x,cost[tot]=c;     }LL gmax(LL a,LL b) { if (a>b) return a; return b; }void dfs1(int x){       vis[x]=true; _max1[x]=_max2[x]=g1[x]=f1[x]=0; LL dis=0;    for (int i=fst[x];i;i=pre[i])      {         int y=to[i];        if (!vis[y])          {             fa[y]=x,fadis[y]=LL(cost[i]); dfs1(y); LL tmp=0;              if (flag[y])               {                flag[x]=true,g1[x]+=g1[y],dis+=LL(cost[i]);                 tmp=g1[y]-f1[y]+cost[i];                if (tmp>_max1[x]) _max2[x]=_max1[x],_max1[x]=tmp;                else if (tmp>_max2[x]) _max2[x]=tmp;              }          }      }    if (!flag[x]) g1[x]=f1[x]=0;    else g1[x]+=(dis<<1),f1[x]=g1[x]-_max1[x];}void dfs2(int x){       vis[x]=true; g2[x]=f2[x]=0; LL dis=0; int son1=0,son2=0;     for (int i=fst[x];i;i=pre[i])      {         int y=to[i];        if (!vis[y] && flag[y]) { if (!son1) son1=y;else son2=y; }      }    if (x!=1)      {        LL tmp=g2[fa[x]]-f2[fa[x]]+fadis[fa[x]];        if (flag[x])           {             g2[x]=g3[fa[x]]-g1[x]-(fadis[x]<<1);            if (_max1[fa[x]]==g1[x]-f1[x]+fadis[x]) f2[x]=g2[x]-gmax(_max2[fa[x]],tmp);            else f2[x]=g2[x]-gmax(_max1[fa[x]],tmp);          }        else g2[x]=g3[fa[x]],f2[x]=f3[fa[x]];         g3[x]=g2[x]+g1[x]+(fadis[x]<<1),f3[x]=g3[x]-gmax(_max1[x],g2[x]-f2[x]+fadis[x]);      }    else g2[x]=f2[x]=0,g3[x]=g1[x],f3[x]=f1[x];     for (int i=fst[x];i;i=pre[i])      {         int y=to[i];        if (!vis[y]) dfs2(y);      }} int main(){     //freopen("kamp.in","r",stdin);    //freopen("kamp.out","w",stdout);    n=Get(),m=Get(); int x,y,c; tot=0;    if (m==0) { puts("0"); return 0; }    for (int i=1;i<=n-1;i++) x=Get(),y=Get(),c=Get(),add(x,y,c);    for (int i=1;i<=m;i++) x=Get(),flag[x]=true;     fa[1]=0,fadis[1]=0; dfs1(1);    for (int i=1;i<=n;i++) vis[i]=false;     dfs2(1);    for (int i=1;i<=n;i++) printf("%lld\n",f3[i]);    return 0; }
0 0
原创粉丝点击