码迷,mamicode.com
首页 > 其他好文 > 详细

[BZOJ3572][HNOI2014]世界树(虚树DP)

时间:2018-01-14 18:39:43      阅读:143      评论:0      收藏:0      [点我收藏+]

标签:简单   技巧   efi   ios   blog   out   return   swap   模拟题   

代码用时1:15

思想比较简单的虚树DP,但细节巨苟,大部分代码都是LCA/DP/虚树模板,真正需要自己写的其实并不多。

写之前要有一个清晰的思路和框架,细节要有一个比较清楚的认识,不能依赖于别人的代码。

附上HNOI2014六道题的总结:

T1:类似最小乘积生成树,KM算法建出凸包即可,套路题。

T2:虚树DP,想到这个应该就不难了。

T3:语文题。技巧:取log后用加法代替乘法。

T4:字符串hash,模拟题。

T5:复杂度玄学的题目,考场上要敢于写这种不确定复杂度的题。

T6:SG函数+分块优化,套路题。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
using namespace std;

const int N=300100;
int dep[N],p[N],f[N][20],nxt[N],to[N],h[N],bel[N],b[N],rem[N];
int n,x,y,Q,top,m,tim,tot,sz[N],ans[N],qq[N],q[N],stk[N],dfn[N];

template<typename T>inline void rd(T &x){
    int t; char ch;
    for (t=0; !isdigit(ch=getchar()); t=(ch==-));
    for (x=ch-0; isdigit(ch=getchar()); x=x*10+ch-0);
    if (t) x=-x;
}

int lca(int a,int b){
    if (dep[a]<dep[b]) swap(a,b);
    int t=dep[a]-dep[b];
    for (int i=19; ~i; i--) if (t & (1<<i)) a=f[a][i];
    if (a==b) return a;
    for (int i=19; ~i; i--) if (f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
    return f[a][0];
}

bool cmp(int a,int b){ return dfn[a]<dfn[b]; }
int get(int u,int v){ return dep[u]+dep[v]-2*dep[lca(u,v)]; }

struct E{
    int h[N],to[N<<1],nxt[N<<1],cnt;
    void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
    void dfs(int x,int fa){
        f[x][0]=fa; dfn[x]=++tim; sz[x]=1;
        rep(i,1,19) f[x][i]=f[f[x][i-1]][i-1];
        For(i,x) if ((k=to[i])!=fa) dep[k]=dep[x]+1,dfs(k,x),sz[x]+=sz[k];
    }
    void dfs2(int x){
        qq[++tot]=x; rem[x]=sz[x];
        For(i,x) if ((k=to[i])!=f[x][0]){
            dfs2(k);
            if (!bel[k]) continue;
            int a=get(bel[k],x),b=get(bel[x],x);
            if (!bel[x] || a<b || (a==b && bel[k]<bel[x])) bel[x]=bel[k];
        }
    }
    void dfs3(int x){
        For(i,x) if ((k=to[i])!=f[x][0]){
            int a=get(bel[x],k),b=get(bel[k],k);
            if (!bel[k]|| a<b || (a==b && bel[x]<bel[k])) bel[k]=bel[x];
            dfs3(k);
        }
    }
    void solve(int a,int b){
        int x=b,k=b;
        for (int i=19; ~i; i--) if (dep[f[x][i]]>dep[a]) x=f[x][i];
        rem[a]-=sz[x];
        if (bel[a]==bel[b]) { ans[bel[a]]+=sz[x]-sz[b]; return; }
        for (int i=19; ~i; i--){
            int t=f[k][i];
            if (dep[t]<=dep[a]) continue;
            int t1=get(bel[a],t),t2=get(bel[b],t);
            if (t2<t1 || (t1==t2 && bel[b]<bel[a])) k=t;
        }
        ans[bel[a]]+=sz[x]-sz[k]; ans[bel[b]]+=sz[k]-sz[b];
    }
    void que(){
        rep(i,1,tot) For(p,qq[i]) solve(k=qq[i],to[p]);
        rep(i,1,tot) ans[bel[qq[i]]]+=rem[qq[i]];
        rep(i,1,m) printf("%d ",ans[q[i]]); puts("");
    }
}G1,G2;

void build(){
    stk[top=1]=p[1];
    rep(i,2,m){
        int v=lca(p[i],stk[top]);
        while (dfn[v]<dfn[stk[top]]){
            if (dfn[v]>=dfn[stk[top-1]]){
                G2.add(v,stk[top]);
                if (v!=stk[--top]) stk[++top]=v;
                break;
            }
            G2.add(stk[top-1],stk[top]); top--;
        }
        stk[++top]=p[i];
    }
    while (top>1) G2.add(stk[top-1],stk[top]),top--;
    G2.dfs2(stk[1]); G2.dfs3(stk[1]); rem[stk[1]]=sz[1]; G2.que();
    rep(i,1,tot) ans[qq[i]]=G2.h[qq[i]]=rem[qq[i]]=bel[qq[i]]=0;
    G2.cnt=tot=0;  
}

int main(){
    freopen("bzoj3572.in","r",stdin);
    freopen("bzoj3572.out","w",stdout);
    rd(n);
    rep(i,1,n-1) rd(x),rd(y),G1.add(x,y),G1.add(y,x);
    G1.dfs(1,0);
    for (rd(Q); Q--; ){
        rd(m);
        rep(i,1,m) scanf("%d",&q[i]),p[i]=q[i],b[q[i]]=1,bel[p[i]]=p[i];
        sort(p+1,p+m+1,cmp); build(); 
    }
    return 0;
}

 

[BZOJ3572][HNOI2014]世界树(虚树DP)

标签:简单   技巧   efi   ios   blog   out   return   swap   模拟题   

原文地址:https://www.cnblogs.com/HocRiser/p/8283730.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!