正题
题目链接:https://www.luogu.com.cn/problem/P4103
题目大意
nnn个点的一棵树,mmm次给出一些点,求最近点对,最远点对和所有点对的距离和。
解题思路
先构造一棵虚树,然后在上面dpdpdp统计答案即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e6+10,T=20,inf=1e18;
struct node{ll to,next;
}a[N*2];
ll n,k,q,tot,st,cnt,ans1,ans2,ans3,dep[N],ls[N];
ll dfn[N],f[N][T+1],siz[N],dp[N][2],s[N],p[N];
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(ll x,ll fa){dep[x]=dep[fa]+1;dfn[x]=++cnt;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;f[y][0]=x;dfs(y,x);}return;
}
ll LCA(ll x,ll y){if(dep[x]>dep[y])swap(x,y);for(ll i=T;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(ll i=T;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];return f[x][0];
}
void ins(ll x){if(!st){s[++st]=x;return;}ll lca=LCA(x,s[st]);while(st>1&&dep[lca]<dep[s[st-1]])addl(s[st-1],s[st]),st--;if(dep[lca]<dep[s[st]])addl(lca,s[st]),st--;if((!st)||(lca!=s[st]))s[++st]=lca;s[++st]=x;return;
}
bool cmp(ll x,ll y)
{return dfn[x]<dfn[y];}
void solve(ll x){if(siz[x])dp[x][0]=dp[x][1]=0;else dp[x][0]=0,dp[x][1]=inf;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to,w=dep[y]-dep[x];solve(y);ans3+=w*siz[y]*(k-siz[y]);if(siz[x]>0){ans1=max(ans1,dp[x][0]+dp[y][0]+w);ans2=min(ans2,dp[x][1]+dp[y][1]+w);}dp[x][0]=max(dp[x][0],dp[y][0]+w);dp[x][1]=min(dp[x][1],dp[y][1]+w);siz[x]+=siz[y];siz[y]=0;}ls[x]=0;return;
}
int main()
{scanf("%lld",&n);for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);} dfs(1,0);for(ll j=1;j<=T;j++)for(ll i=1;i<=n;i++)f[i][j]=f[f[i][j-1]][j-1];scanf("%lld",&q);memset(ls,0,sizeof(ls));while(q--){scanf("%lld",&k);st=tot=0;for(ll i=1;i<=k;i++){scanf("%lld",&p[i]);siz[p[i]]++;}sort(p+1,p+1+k,cmp);if(p[1]!=1)s[++st]=1;for(ll i=1;i<=k;i++)ins(p[i]);while(st>1)addl(s[st-1],s[st]),st--;ans1=ans3=0;ans2=inf;solve(1);siz[1]=0;printf("%lld %lld %lld\n",ans3,ans2,ans1);}return 0;
}