zoukankan      html  css  js  c++  java
  • [HEOI2014][bzoj3611] 大工程 [虚树+dp]

    题面:

    传送门

    思路:

    又是一道虚树入门级的题目,但是这道题的实际难点在于dp

    首先,这道题是可以点分治做的,而且因为6s时限随便浪,所以写点分治也不是不可以

    但是,dp因为$Oleft(n ight)$的高效率,依然最好优先考虑

    对于这道题的dp,我们首先记录几个数组:

    siz[u]表示以u为根(包括u)的子树中,询问点的总个数

    minn[u]表示子树中最短的一条由子树根到询问点的路径,maxn[u]则是最长的

    sum[u]则代表这棵子树中所有要求路径的长度总和

    最小路径和最大路径的dp都比较简单了

    每次进入子树dp结束以后,用minn[v]更新minn[u],然后用minn[u]+minn[v]+dis(u,v)更新答案

    maxn同理

    注意这里虽然是单位边权,但是建完虚树以后边的长度便不再固定是1了

    sum的dp比较麻烦一点

    每次子树v的dp结束以后,先把siz[u]+=siz[v],然后sum[u]+=sum[v]+siz[v]*(m-siz[v])*dis(u,v)

    这里的m是本次询问的所有点数

    这里相当于是把v子树中的所有路径(内部的路径)的值,加上经过(u,v)这条边的路径总数乘以dis(u,v),然后加入到了总答案中

    这样每个sum都会包含每条边应该经过的次数,答案是正确的

    虚树建立起来以后,直接dp就可以了。注意这道题里面不能固定以1作为起点,因为路径很可能不经过1,所以需要另一种建立虚树的方法,详见代码

    Code:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #define ll long long
      6 #define inf 1e18
      7 using namespace std;
      8 inline ll read(){
      9     ll re=0,flag=1;char ch=getchar();
     10     while(ch>'9'||ch<'0'){
     11         if(ch=='-') flag=-1;
     12         ch=getchar();
     13     }
     14     while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
     15     return re*flag;
     16 }
     17 ll n,m,dep[1000010],st[1000010][22],dfn[1000010],clk;
     18 ll sum[1000010],minn[1000010],maxn[1000010],siz[1000010];
     19 struct graph{
     20     ll first[1000010],cnt;
     21     struct edge{
     22         ll to,next;
     23     }a[2000010];
     24     graph(){
     25         memset(first,-1,sizeof(first));cnt=0;
     26     }
     27     void init(){cnt=0;}
     28     inline void add(ll u,ll v){
     29         if(u==v) return;
     30         a[++cnt]=(edge){v,first[u]};first[u]=cnt;
     31     }
     32 }G,g;
     33 void dfs(ll u,ll f){
     34     ll i,v;
     35     dep[u]=dep[f]+1;dfn[u]=++clk;st[u][0]=f;
     36     for(i=G.first[u];~i;i=G.a[i].next){
     37         v=G.a[i].to;
     38         if(v==f) continue;
     39         dfs(v,u);
     40     }
     41 }
     42 void ST(){
     43     ll i,j;
     44     for(j=1;j<=21;j++) for(i=1;i<=n;i++) st[i][j]=st[st[i][j-1]][j-1];
     45 }
     46 ll lca(ll l,ll r){
     47     if(dep[l]>dep[r]) swap(l,r);
     48     ll i;
     49     for(i=21;i>=0;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
     50     if(l==r) return l;
     51     for(i=21;i>=0;i--)
     52         if(st[l][i]!=st[r][i]){
     53             l=st[l][i];
     54             r=st[r][i];
     55         }
     56     return st[l][0];
     57 }
     58 ll q[1000010],s[1000010],top,ans1,ans2,ans3;
     59 bool vis[1000010];
     60 bool cmp(ll l,ll r){
     61     return dfn[l]<dfn[r];
     62 }
     63 void dp(ll u){
     64     ll i,v,dis;
     65     sum[u]=0;minn[u]=inf;maxn[u]=0;siz[u]=(vis[u]);
     66     for(i=g.first[u];~i;i=g.a[i].next){
     67         v=g.a[i].to;g.first[u]=g.a[i].next;
     68         dp(v);dis=dep[v]-dep[u];
     69         ans2=min(ans2,minn[u]+minn[v]+dis);minn[u]=min(minn[u],minn[v]+dis);
     70         ans3=max(ans3,maxn[u]+maxn[v]+dis);maxn[u]=max(maxn[u],maxn[v]+dis);
     71         siz[u]+=siz[v];
     72         sum[u]+=sum[v]+siz[v]*(m-siz[v])*dis;
     73     }
     74     if(vis[u]) vis[u]=0,ans2=min(ans2,minn[u]),ans3=max(ans3,maxn[u]),minn[u]=0;
     75 }
     76 void solve(){
     77     m=read();ll i,j,grand;g.init();
     78     for(i=1;i<=m;i++) q[i]=read(),vis[q[i]]=1;
     79     sort(q+1,q+m+1,cmp);top=0;
     80     for(i=1;i<=m;i++){
     81         if(!top){s[++top]=q[i];continue;}//因为现在没有一个1号结点压在栈底不可能弹出,所以需要检测栈是否为空
     82         grand=lca(q[i],s[top]);
     83         while(1){
     84             if(dep[s[top-1]]<=dep[grand]){
     85                 g.add(grand,s[top--]);
     86                 if(s[top]!=grand) s[++top]=grand;
     87                 break;
     88             }
     89             g.add(s[top-1],s[top]);top--;
     90         }
     91         if(s[top]!=q[i]) s[++top]=q[i];
     92     }
     93     while(--top>=1) g.add(s[top],s[top+1]);
     94     ans1=ans3=0;ans2=inf;
     95     dp(s[1]);//这里从栈中最后剩下的元素开始dp,因为它就是整棵虚树的根了
     96     printf("%lld %lld %lld
    ",sum[s[1]],ans2,ans3);
     97     return;
     98 }
     99 int main(){
    100     ll i,j,t1,t2;n=read();
    101     for(i=1;i<n;i++){
    102         t1=read(),t2=read();
    103         G.add(t1,t2);G.add(t2,t1);
    104     }
    105     dfs(1,0);ST();
    106     ll q=read();
    107     for(i=1;i<=q;i++) solve();
    108 }
  • 相关阅读:
    hdu 1290 献给杭电五十周年校庆的礼物 (DP)
    hdu 3123 GCC (数学)
    hdu 1207 汉诺塔II (DP)
    hdu 1267 下沙的沙子有几粒? (DP)
    hdu 1249 三角形 (DP)
    hdu 2132 An easy problem (递推)
    hdu 2139 Calculate the formula (递推)
    hdu 1284 钱币兑换问题 (DP)
    hdu 4151 The Special Number (DP)
    hdu 1143 Tri Tiling (DP)
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8570043.html
Copyright © 2011-2022 走看看