BZOJ3611: [Heoi2014]大工程
题解:虚树模板题 维护虚树 跑树dp即可
/************************************************************** Problem: 3611 User: c20161007 Language: C++ Result: Accepted Time:15176 ms Memory:357336 kb ****************************************************************/ #include <bits/stdc++.h> #define ll long long const int MAXN=1e6+10; using namespace std; const ll inf=1e18; vector<int>vec[MAXN]; int f[MAXN][21];ll sum[MAXN][21];int dep[MAXN]; int p[MAXN],cnt; int st[MAXN],tot; vector<int>V; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return f*x; } void dfs(int v,int pre,int deep){ dep[v]=deep;f[v][0]=pre;p[v]=++cnt; for(int i=0;i<vec[v].size();i++){ int u=vec[v][i]; if(u!=pre){ sum[u][0]=1; dfs(u,v,deep+1); } } } void dfs1(int v){ for(int i=1;i<=20;i++)sum[v][i]=sum[f[v][i-1]][i-1]+sum[v][i-1],f[v][i]=f[f[v][i-1]][i-1]; for(int i=0;i<vec[v].size();i++){ int u=vec[v][i]; if(u!=f[v][0])dfs1(u); } } int Lca(int u,int v){ if(dep[u]<dep[v])swap(u,v); int tmp=dep[u]-dep[v]; for(int i=0;i<=20;i++)if(tmp&(1<<i))u=f[u][i]; if(u==v)return u; for(int i=20;i>=0;i--){ if(f[u][i]!=f[v][i]){ u=f[u][i]; v=f[v][i]; } } return f[u][0]; } void built(int x){ V.push_back(x); if(!tot){st[++tot]=x;return ;} int lca=Lca(x,st[tot]); // cout<<lca<<" "<<x<<" "<<st[tot]<<endl; while(tot>1&&dep[lca]<dep[st[tot-1]]){ vec[st[tot]].push_back(st[tot-1]); vec[st[tot-1]].push_back(st[tot]); tot--; } if(dep[st[tot]]>dep[lca]){ // cout<<lca<<"::::"<<st[tot]<<endl; vec[st[tot]].push_back(lca); vec[lca].push_back(st[tot]); tot--; V.push_back(lca); } // cout<<tot<<"====="<<endl; if(!tot||dep[lca]>dep[st[tot]])st[++tot]=lca; // cout<<st[tot]<<endl; st[++tot]=x; } ll dist(int u,int v){ ll res=0; // cout<<u<<"::::::"<<v<<endl; for(int i=20;i>=0;i--){ if(dep[f[u][i]]>dep[v]){ res+=sum[u][i]; // cout<<u<<"::::"<<f[u][i]<<" "<<res<<endl; u=f[u][i]; } } res+=sum[u][0]; return res; } ll _f[MAXN],__f[MAXN],d[MAXN],_d[MAXN]; int num[MAXN]; bool vis[MAXN]; ll ans; int po; void dfs(int x,int pre){ // cout<<x<<" "<<pre<<endl; __f[x]=_d[x]=inf;_f[x]=d[x]=0; if(vis[x])num[x]=1; for(int i=0;i<vec[x].size();i++){ if(vec[x][i]!=pre){ dfs(vec[x][i],x); num[x]+=num[vec[x][i]]; ll vul=dist(vec[x][i],x); ans+=1ll*vul*num[vec[x][i]]*(po-num[vec[x][i]]); if(d[vec[x][i]]||vis[vec[x][i]]){ _f[x]=max(_f[x],d[x]+d[vec[x][i]]+vul); _f[x]=max(_f[x],_f[vec[x][i]]); d[x]=max(d[x],d[vec[x][i]]+vul); __f[x]=min(__f[x],__f[vec[x][i]]); __f[x]=min(__f[x],_d[x]+_d[vec[x][i]]+vul); _d[x]=min(_d[x],_d[vec[x][i]]+vul); if(vis[x])_f[x]=max(_f[x],d[vec[x][i]]+vul),__f[x]=min(__f[x],_d[vec[x][i]]+vul); } } } if(vis[x])_d[x]=0; if(!d[x])_d[x]=0; } int n,m; bool cmp(int aa,int bb){return p[aa]<p[bb];} int main(){ n=read();int u,v; for(int i=1;i<n;i++)u=read(),v=read(),vec[u].push_back(v),vec[v].push_back(u); // cout<<"sb"<<endl; dfs(1,0,0);dfs1(1);int k; for(int i=1;i<=n;i++)vec[i].clear(); m=read(); for(int i=1;i<=m;i++){ k=read();tot=0;ans=0;po=k; for(int i=1;i<=k;i++)v=read(),V.push_back(v),vis[v]=1; sort(V.begin(),V.end(),cmp); for(int i=1;i<=k;i++)built(V[i-1]); // for(int i=tot;i>=1;i--)cout<<st[i]<<" "; // cout<<endl; while(tot>1){vec[st[tot]].push_back(st[tot-1]),vec[st[tot-1]].push_back(st[tot]);tot--;} // cout<<vec[1].size()<<" "<<vec[4].size()<<" "<<vec[5].size()<<endl; // cout<<st[tot]<<endl; dfs(st[tot],f[st[tot]][0]); printf("%lld %lld %lld\n",ans,__f[st[tot]],_f[st[tot]]); //V.clear(); for(int i=0;i<V.size();i++)vec[V[i]].clear(),vis[V[i]]=0,num[V[i]]=0; V.clear(); } return 0; }
3611: [Heoi2014]大工程
Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 2194 Solved: 945
[Submit][Status][Discuss]
Description
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
1.这些新通道的代价和
2.这些新通道中代价最小的是多少
3.这些新通道中代价最大的是多少
Input
第一行 n 表示点数。
接下来 n-1 行,每行两个数 a,b 表示 a 和 b 之间有一条边。
点从 1 开始标号。 接下来一行 q 表示计划数。
对每个计划有 2 行,第一行 k 表示这个计划选中了几个点。
第二行用空格隔开的 k 个互不相同的数表示选了哪 k 个点。
Output
输出 q 行,每行三个数分别表示代价和,最小代价,最大代价。
Sample Input
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
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
Sample Output
3 3 3
6 6 6
1 1 1
2 2 2
2 2 2
6 6 6
1 1 1
2 2 2
2 2 2
HINT
n<=1000000
q<=50000并且保证所有k之和<=2*n