[HEOI2014]大工程
题目描述
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。现在对于每个计划,我们想知道: 1.这些新通道的代价和 2.这些新通道中代价最小的是多少 3.这些新通道中代价最大的是多少
输入输出格式
输入格式:
第一行 n 表示点数。
接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。点从 1 开始标号。
接下来一行 q 表示计划数。对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
输出格式:
输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。
输入输出样例
输入样例#1:
10 2 1 3 2 4 1 5 2 6 4 7 5 8 6 9 7 10 9 5 2 5 4 2 10 4 2 5 2 2 6 1 2 6 1
输出样例#1:
3 3 3 6 6 6 1 1 1 2 2 2
2 2 2
说明
对于第 1,2 个点: n<=10000
对于第 3,4,5 个点: n<=100000,交通网络构成一条链
对于第 6,7 个点: n<=100000
对于第 8,9,10 个点: n<=1000000
对于所有数据, q<=50000并且保证所有k之和<=2*n
像这种询问很多但是每个询问的关键点数都不多的题大多数都要用到虚树。
虚树就是只含关键点和关键点在原树中的LCA的新构造的树。
然后建完虚树就可以直接dp了(虚树模板+树上dp模板)
不过写起来挺劲的hhhh,又调了好久(为什么最近总是把数组下标和权值搞混gg)
(可能今天写了一天数据结构比较蛋疼hhhh)
/************************************************************** Problem: 3611 User: JYYHH Language: C++ Result: Accepted Time:7872 ms Memory:203356 kb ****************************************************************/ #include<bits/stdc++.h> #define ll long long #define maxn 1000005 #define pb push_back using namespace std; vector<int> g[maxn]; bool ispt[maxn]; int to[maxn],ne[maxn],mxl,mnl; int hd[maxn],num,n,d[maxn],sz[maxn]; int len,que[maxn],dc=0,q,vis[maxn]; int st[maxn],tp,dfn[maxn],f[maxn][23]; int mn[maxn],mx[maxn],ci[30]; ll tot[maxn],tt; inline bool cmp(int x,int y){ return dfn[x]<dfn[y]; } inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num; } inline int read(){ int x=0; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x; } void dfs(int x,int fa){ int to; dfn[x]=++dc; for(int i=1;ci[i]<=d[x];i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=g[x].size()-1;i>=0;i--){ to=g[x][i]; if(to==fa) continue; d[to]=d[x]+1,f[to][0]=x; dfs(to,x); } } inline int LCA(int a,int b){ if(d[a]<d[b]) swap(a,b); int dt=d[a]-d[b]; for(int i=0;ci[i]<=dt;i++) if(ci[i]&dt) a=f[a][i]; if(a==b) return a; int s=log(d[a])/log(2)+1; for(;s>=0;s--){ if(ci[s]>d[a]) continue; if(f[a][s]!=f[b][s]) a=f[a][s],b=f[b][s]; } return f[a][0]; } inline void clear(int x){ hd[x]=sz[x]=tot[x]=0; } void dp(int x){ if(ispt[x]){ sz[x]=1,mn[x]=mx[x]=0; }else mn[x]=1<<25,mx[x]=-1<<25; for(int i=hd[x];i;i=ne[i]){ dp(to[i]); int lgh=d[to[i]]-d[x]; mxl=max(mxl,mx[x]+mx[to[i]]+lgh),mx[x]=max(mx[x],mx[to[i]]+lgh); mnl=min(mnl,mn[x]+mn[to[i]]+lgh),mn[x]=min(mn[x],mn[to[i]]+lgh); tt+=(ll)sz[to[i]]*sz[x]*(ll)lgh+(ll)sz[x]*tot[to[i]]+(ll)sz[to[i]]*tot[x]; tot[x]+=(ll)tot[to[i]]+sz[to[i]]*(ll)lgh; sz[x]+=sz[to[i]]; } // printf("%d %d %d %lld\n",x,mn[x],mx[x],tot[x]); } inline void solve(){ sort(que+1,que+len+1,cmp); st[tp=1]=1,num=0,vis[1]=q,clear(1); for(int i=1;i<=len;i++){ ispt[que[i]]=1; if(que[i]==1) continue; if(vis[que[i]]!=q) vis[que[i]]=q,clear(que[i]); int la=LCA(que[i],st[tp]); if(la!=st[tp]){ if(vis[la]!=q) vis[la]=q,clear(la); while(tp>1&&d[st[tp-1]]>=d[la]) tp--,add(st[tp],st[tp+1]); if(st[tp]!=la) add(la,st[tp]),st[tp]=la; } st[++tp]=que[i]; } for(;tp>1;tp--) add(st[tp-1],st[tp]); dp(1); for(int i=1;i<=len;i++) ispt[que[i]]=0; } int main(){ ci[0]=1; for(int i=1;i<=23;i++) ci[i]=ci[i-1]+ci[i-1]; n=read(); int uu,vv; for(int i=1;i<n;i++){ uu=read(),vv=read(); g[uu].pb(vv),g[vv].pb(uu); } d[1]=0,f[1][0]=1; dfs(1,1); q=read(); while(q--){ tt=0,mxl=0,mnl=1<<30; len=read(); for(int i=1;i<=len;i++) que[i]=read(); solve(); printf("%lld %d %d\n",tt,mnl,mxl); } return 0; }
我爱学习,学习使我快乐