• [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 }
  • 相关阅读:
    JQuery easyui (4)Tooltip (提示组件) 组件
    JQuery easyui (3) Resizable(调整大小)组件
    洛谷 P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
    2019 ACM-ICPC 上海网络赛 B. Light bulbs (差分)
    POJ 1474 Video Surveillance (半平面交)
    HDU 5119 Happy Matt Friends (背包DP + 滚动数组)
    洛谷 P1613 跑路 (倍增 + DP + 最短路)
    POJ 1279 Art Gallery (半平面交)
    POJ 3130 How I Mathematician Wonder What You Are! (半平面交)
    POJ 2451 Uyuw's Concert (半平面交)
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8570043.html
Copyright © 2020-2023  润新知