1. 程式人生 > >洛谷4103 HEOI2014大工程(虛樹+dp)

洛谷4103 HEOI2014大工程(虛樹+dp)

題目連結

又是一道虛樹好題啊

我們建出來虛樹,然後考慮dp過程,我們分別令 s u m [ x ] , m

n d i s [ x ] , m x d
i s [ x ] , s i z e [
x ] sum[x],mndis[x],mxdis[x],size[x] 為子樹內的路徑長度和,最短鏈,最長鏈,子樹內關鍵點個數。

對於一個關鍵點,首先他的 s i z e = 1 , m n d i s = 0 size=1,mndis=0

我們考慮怎麼合併,首先我們可以直接維護三個值表示最終的答案。如果說當前的點 s i z e [ x ] > 0 size[x]>0 ,那麼我們就可以每次用他和新的子樹進行更新ans,然後合併
QWQ
其實合併就差不多類似的方式
主要是 s u m sum 合併的時候,你要用 s i z e [ x ] s i z e [ p ] v a l [ i ] size[x]*size[p]*val[i] 當前這條邊會被算的貢獻, s i z e [ x ] s u m [ p ] size[x]*sum[p] ,每條 p p 的路徑,都會到 x x 中的所有關鍵點, s i z e [ p ] s u m [ x ] size[p]*sum[x] 這個也是同理

其他的應該也就差不多了

直接上程式碼

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 3e6+1e2;
const int maxm = 4e6+1e2;
const int inf = 1e18;
int point[maxn],nxt[maxm],to[maxm];
int deep[maxn],dfn[maxn];
int sum[maxn];
int n,m,tot,top,cnt;
int f[1010000][21];
int k,size[maxn],tag[maxn];
int s[maxn];
int mxdis[maxn],mndis[maxn];
int mx,mn,ymh;
int a[maxn];
int val[maxn];
void addedge(int x,int y,int w)
{
 //cout<<"***"<<" "<<x<<" "<<y<<endl;
 nxt[++cnt]=point[x];
 to[cnt]=y;
 val[cnt]=w;
 point[x]=cnt;
}
void dfs(int x,int fa,int dep)
{
 deep[x]=dep;
 dfn[x]=++tot;
 for (int i=point[x];i;i=nxt[i])
 {
  int p = to[i];
  if (p==fa) continue;
  f[p][0]=x;
  dfs(p,x,dep+1);
 }
}
void init()
{
 for (int j=1;j<=20;j++)
   for (int i=1;i<=n;i++)
     f[i][j]=f[f[i][j-1]][j-1];
}
int go_up(int x,int d)
{
 for (int i=0;i<=20;i++)
 {
  if (d&(1<<i))
  {
   x=f[x][i];
  }
 }
 return x;
}
int lca(int x,int y)
{
 if (deep[x]>deep[y]) x=go_up(x,deep[x]-deep[y]);
 else y=go_up(y,deep[y]-deep[x]);
 if (x==y) return x;
 for (int i=20;i>=0;i--)
 {
  if (f[x][i]!=f[y][i])
  {
   x=f[x][i];
   y=f[y][i];
  }
 }
 return f[x][0];
}
bool cmp(int a,int b)
{
 return dfn[a]<dfn[b];
}
void solve()
{
// memset(point,0,sizeof(point));
 sort(a+1,a+1+k,cmp);
    cnt=0;
    top=1;
    s[top]=1;
    for (int i=1;i<=k;i++)
    {
     int l = lca(s[top],a[i]);
  if (l!=s[top])
  {
     while (top>1)
     {
        if (dfn[s[top-1]]>dfn[l])
        {
           addedge(s[top-1],s[top],deep[s[top]]-deep[s[top-1]]);
      top--; 
     }
     else
     {
       if (dfn[s[top-1]]==dfn[l])
       {
        addedge(s[top-1],s[top],deep[s[top]]-deep[s[top-1]]);
     top--;
     break;       
     }
     else
     {
      addedge(l,s[top],deep[s[top]]-deep[l]);
      s[top]=l;
      break;
     }
     }
     } 
  }
  if (s[top]!=a[i]) s[++top]=a[i]; 
 }
 while (top>1)
 {
  addedge(s[top-1],s[top],deep[s[top]]-deep[s[top-1]]);
  top--;
 }
}
void dp(int x,int flag)
{
 mndis[x]=inf;
 mxdis[x]=0;
 size[x]=0;
 sum[x]=0;
 if (tag[x]==flag) 
 {
  size[x]=1;
  mndis[x]=0;
 }
 for (int &i=point[x];i;i=nxt[i])
 {
  int p = to[i];
  int now = val[i];
        dp(p,flag);
        if (size[x]>0)
        {
         ymh=ymh+size[p]*size[x]*now+size[p]*sum[x]+size[x]*sum[p];
         mx=max(mx,mxdis[x]+mxdis[p]+now);
         mn=min(mn,mndis[x]+mndis[p]+now);
  }
  mndis[x]=min(mndis[x],mndis[p]+now);
  mxdis[x]=max(mxdis[p]+now,mxdis[x]);
  sum[x]+=sum[p]+now*size[p]; 
  size[x]+=size[p];
 }
 //cout<<mndis[x]<<" "<<mxdis[x]<<" "<<size[x]<<" "<<sum[x]<<endl;
}
signed main()
{
  n=read();
  for (int i=1;i<n;i++)
  {
    int x=read(),y=read();
    addedge(x,y,0);
    addedge(y,x,0);
  }
  dfs(1,0,1);
  init();
  memset(point,0,sizeof(point));
  m=read();
  for (int i=1;i<=m;i++)
  {
    ymh=0;
    mn=inf;
    mx=0;
    k=read();
    for (int j=1;j<=k;j++) a[j]=read(),tag[a[j]]=i;
    solve();
    dp(1,i);
    cout<<ymh<<" "<<mn<<" "<<mx<<"\n";
  }
  return 0;
}