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;
}