BZOJ 3611:大工程

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3611

分析:

看到m总和的限制不难想到虚树,我们先把虚树建出来,然后问题就是考虑如何DP了。

对于最长链和最短链我们都可以通过枚举它们的LCA来计算,考虑总和,总共有\(C(n,2)\)条边,暴力统计肯定不行。

所以我们考虑每条边在答案中的“贡献”,它的贡献即为 边权 * 分别在这条边两边的节点数乘积,而结点个数和也可以用DP解决,这样复杂度就变成了\(O(m)\)

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

using namespace std;

typedef long long LL;

const int maxn=2000000+23,maxlogn=20+1;
const int INF=(1<<30);

struct Edge{
 int v,val,next;
 Edge(int v=0,int val=0,int next=0):v(v),val(val),next(next) {}
} E[maxn];

int n,m,vs,size,Esize,w,ans2,ans3,mark,p,x,y;

int last[maxn],depth[maxn],F[maxn][maxlogn],L[maxn],R[maxn],st[maxn],h[maxn],maxi[maxn],mini[maxn],sum[maxn];

LL ans1;

bool vis[maxn];

//----------------

bool cmp(int x,int y) {return L[x]<L[y];}

void addedge(int x,int y) {E[++Esize]=Edge(y,0,last[x]),last[x]=Esize;}

void addpdge(int x,int y,int val)
{
 if (x==y) return;
 E[++Esize]=Edge(y,val,last[x]),last[x]=Esize;
}

void DFS(int k,int fa,int dep)
{
 depth[k]=dep,F[k][0]=fa,L[k]=++vs;
 for (int i=last[k];i;i=E[i].next)
  if (E[i].v!=fa) DFS(E[i].v,k,dep+1);
 R[k]=vs;
}

void LCA_init()
{
 rep(k,1,maxlogn-1)
  rep(i,1,n) if (F[i][k-1]==-1) F[i][k]=-1; else F[i][k]=F[F[i][k-1]][k-1];
}

int query(int x,int y)
{
 if (depth[x]>depth[y]) swap(x,y);
 rep(k,0,maxlogn-1) if (((depth[y]-depth[x])>>k)&1) y=F[y][k];

 if (x==y) return x;

 dep(k,maxlogn-1,0) if (F[x][k]!=F[y][k]) x=F[x][k],y=F[y][k];
 return F[x][0];
}

void dp(int k)
{
 sum[k]=(int)(vis[k]),mini[k]=(vis[k])?(0):INF,maxi[k]=0;
 
 for (int i=last[k];i;i=E[i].next)
 {
  dp(E[i].v);

  sum[k]+=sum[E[i].v],ans1+=(LL)(E[i].val)*(LL)(p-sum[E[i].v])*(LL)(sum[E[i].v]);

  if (vis[k]) ans2=min(ans2,mini[E[i].v]+E[i].val),ans3=max(ans3,maxi[E[i].v]+E[i].val);
  ans2=min(ans2,mini[k]+mini[E[i].v]+E[i].val),ans3=max(ans3,maxi[k]+maxi[E[i].v]+E[i].val);

  mini[k]=min(mini[k],mini[E[i].v]+E[i].val),maxi[k]=max(maxi[k],maxi[E[i].v]+E[i].val);
 }
 last[k]=0;
}

int main()
{
 scanf("%d",&n);
 
 vs=Esize=0;
 rep(i,1,n-1)
 {
  scanf("%d%d",&x,&y);
  addedge(x,y),addedge(y,x);
 }
 
 DFS(1,-1,0); LCA_init();
 
 scanf("%d",&m);

 memset(last,0,sizeof(last));

 rep(i,1,m)
 {
  scanf("%d",&p);
  rep(j,1,p) scanf("%d",&h[j]),vis[h[j]]=1;
  
  sort(h+1,h+p+1,cmp);

  Esize=0,size=1,st[1]=1,mark=1;
  ans1=0,ans2=INF,ans3=-INF;

  rep(j,1,p)
  {
   if (h[j]==h[j-1]) continue;
   
   w=query(h[j],st[size]);
   
   while ((L[w]<L[st[size]])&&(R[w]>R[st[size]]))
   {
    if ((L[w]>=L[st[size-1]])&&(R[st[size-1]]>=R[w]))
    {
     addpdge(w,st[size],depth[st[size]]-depth[w]),size--;
     break;
    }
    addpdge(st[size-1],st[size],depth[st[size]]-depth[st[size-1]]);
    size--;
   }
   
   if (st[size]!=w) st[++size]=w;
   st[++size]=h[j];

   if ((j>1)&&(w==1)) mark=0;
  }
  
  dep(j,size-1,mark+1) addpdge(st[j],st[j+1],depth[st[j+1]]-depth[st[j]]);
 
  dp(st[mark+1]); 

  printf("%lld %d %d\n",ans1,ans2,ans3);
  rep(j,1,p) vis[h[j]]=0;
 }
 
 return 0;
}

posted @ 2016-12-26 20:54  Krew  阅读(89)  评论(0编辑  收藏  举报