zoukankan      html  css  js  c++  java
  • 环套树

    bzoj1040 骑士

    题目大意:给出每个骑士的攻击力和痛恨的人,每个骑士不能和自己痛恨的人一起出现,选一些骑士使得他们的攻击力最大。

    思路:因为每个骑士恨一个人,所以是一个环套树森林(注意n点n边的联通图可能是环套树森林!!!),虽然题目是单向边,但等同于双向边。对于环套树dp的做法,可以搜到环后,把环从一处断开,对于这道题目,对断开后的两点分别为根做树型dp(要求根不能选),加给答案。但是要注意二元环的情况,我们可以对于二元环只加一次无向边(其实是两条有向边),因为二元环完全可以当作有边相连的两点处理,这个时候可能会出现一棵没有环的树,我们需要在退回到dfs起点的那个点的时候人为的做一下dp。

    一定要十分注意二元环的情况!!!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define maxnode 1000005
    using namespace std;
    int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},tot=0,x=0,y=0,ai[maxnode]={0};
    LL val[maxnode]={0},f[maxnode][2]={0},ans=0;
    bool visit[maxnode]={false},ff;
    void add(int u,int v)
    {
        ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;
        ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;
    }
    void dp(int u,int fa)
    {
        int i,j;
        f[u][1]=val[u];visit[u]=true;
        for (i=point[u];i;i=next[i])
        {
            if (en[i]!=fa)
            {
                if ((u==y&&en[i]==x)||(u==x&&en[i]==y)) continue;
                dp(en[i],u);
                f[u][0]+=max(f[en[i]][0],f[en[i]][1]);
                f[u][1]+=f[en[i]][0];
            }
        }
    }
    void dfs(int u,int fa)
    {
        int i,j;LL ci=0;
        visit[u]=true;
        for (i=point[u];i;i=next[i])
        {
            if (en[i]!=fa)
            {
                if (visit[en[i]])
                {
                    x=u;y=en[i];
                    memset(f,0,sizeof(f));
                    dp(x,y);ci=max(ci,f[x][0]);
                    memset(f,0,sizeof(f));
                    swap(x,y);dp(x,y);ci=max(ci,f[x][0]);
                    ans+=ci;ff=true;return;
                }
                else
                {
                   dfs(en[i],u);if (ff) return;
                }
            }
        }
        if (fa==0)
        {
            x=y=0;dp(u,0);
            ans+=max(f[u][0],f[u][1]);
        }
    }
    int main()
    {
        int n,i,j;
        scanf("%d",&n);
        for (i=1;i<=n;++i) scanf("%I64d%d",&val[i],&ai[i]);
        for (i=1;i<=n;++i)
            if (ai[ai[i]]!=i||ai[i]>i) add(i,ai[i]);
        for (i=1;i<=n;++i)
          if (!visit[i]){ff=false;dfs(i,0);}
        printf("%I64d
    ",ans);
    }
    View Code

    bzoj2878 迷失游乐园(!!

    题目大意:给出一棵环套树,随机选一个点开始走,等概率走向周围没有经过的点,问走的路径长度期望。

    思路:树上的比较好求,从终点开始走,更新出每个点的up和down,gi[u][0]=sigma 1/(du[u]-1)*(gi[v][0]+va[i]),最后一个点的时候(也就是起点)是/du[u]。有环的时候,先找出所有外向树的up,用这个up去更新其他的up(中间要用另一个数组存,防止改变最初的up)。有环的时候的概率是(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc。cc表示环上选这条路径的概率,ci表示环上链的权值和,ci后面乘的式子是因为外向树中所带来的概率的和不是树上的(du[u]-1)/du[u],而是(du[u]-2)/du[u],所以有所改变。对于环上相邻两点绕了一圈的情况终点的处的概率是1/(du[u]-2),因为环上的两个点都不能走。

    注意:一定要考虑好概率的转移和环上相邻点的特殊情况。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100005
    #define M 200005
    #define LD double
    #define eps 1e-9
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    int cmp(LD x,LD y){
        if (x-y>eps) return 1;
        if (y-x>eps) return -1;
        return 0;}
    int n,m,rt,point[N]={0},next[M],en[M],tot,du[N]={0},fe[N]={0},hu[N],hz=0,zh[N],zt=0;
    LD va[M],fi[N],gi[N][2],hv[N];
    bool oh[N]={false},hh=false,vi[N]={false};
    void add(int u,int v,LD w){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w;
        ++du[u];++du[v];
    }
    void up(int u,int fa){
        int i,v;gi[u][0]=0.;
        for (i=point[u];i;i=next[i]){
            if ((v=en[i])==fa) continue;
            up(v,u);gi[u][0]+=1./(du[u]-1)*(gi[v][0]+va[i]);
        }
    }
    void down(int u,int fa,LD vv){
        int i,v;fi[u]=0.;gi[u][1]=gi[u][0];
        if (fa){
            fi[u]+=1./du[u]*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);
            if (du[u]>1) gi[u][1]+=1./(du[u]-1)*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);
        }for (i=point[u];i;i=next[i]){
            if ((v=en[i])==fa) continue;
            down(v,u,va[i]);
            fi[u]+=1./du[u]*(gi[v][0]+va[i]);
        }
    }
    void geth(int x){
        hz=0;
        for (;;--zt){
            oh[zh[zt]]=true;
            hu[++hz]=zh[zt];
            hv[hz]=va[fe[zh[zt]]];
            if (zh[zt]==x) break;
        }
    }
    void find(int u){
        int i,v;zh[++zt]=u;
        vi[u]=true;
        for (i=point[u];i;i=next[i]){
            v=en[i];
            if (i==fe[u]||(i==(fe[u]^1))) continue;
            if (vi[v]){
                geth(v);hh=true;
                hv[hz]=va[i];return;
            }fe[v]=i;find(v);
            if (hh) return;
        }--zt;
    }
    void gup(int u){
        int i,v;gi[u][0]=0.;
        vi[u]=true;
        for (i=point[u];i;i=next[i]){
            if (vi[v=en[i]]) continue;
            gup(v);gi[u][0]+=1./(du[u]-1)*(gi[v][0]+va[i]);
        }
    }
    void gdown(int u,int fa,LD vv){
        int i,v;gi[u][1]=gi[u][0];
        if (fa){
            fi[u]+=1./du[u]*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);
            if (du[u]>1) gi[u][1]+=1./(du[u]-1)*(gi[fa][1]-(1./(du[fa]-1)*(gi[u][0]+vv))+vv);
        }for (i=point[u];i;i=next[i]){
            v=en[i];
            if (oh[v]||v==fa) continue;
            gdown(v,u,va[i]);
            fi[u]+=1./du[u]*(gi[v][0]+va[i]);
        }
    }
    int main(){
        int i,j,u,v,w,la;LD ans=0.,ci,cc;
        n=in();m=in();tot=1;
        for (i=1;i<=m;++i){
            u=in();v=in();w=in();
            add(u,v,(LD)w);
        }if (m==n-1){
            for (i=1;i<=n;++i)
                if (du[i]>1){rt=i;break;}
            up(rt,0);down(rt,0,0.);
            for (i=1;i<=n;++i) ans+=1./n*fi[i];
        }else{
            find(1);
            for (i=1;i<=n;++i){
                vi[i]=oh[i];
                fi[i]=gi[i][0]=gi[i][1]=0.;
            }for (i=1;i<=n;++i)
                if (oh[i]) gup(i);
            for (i=1;i<=hz;++i){
                u=hu[i];ci=0.;cc=1.;
                gi[u][1]+=gi[u][0];
                for (la=i,j=i%hz+1;j!=i;la=j,j=j%hz+1){
                    ci+=hv[la];cc/=1.*(du[hu[j]]-1);
                    if (cmp(gi[u][0],0.)>0){
                        if (j%hz+1!=i){
                            fi[hu[j]]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*
                                        cc*(du[hu[j]]-1)/du[hu[j]];
                            gi[hu[j]][1]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc;
                        }else{
                            fi[hu[j]]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc*(du[hu[j]]-1)/du[hu[j]];
                            gi[hu[j]][1]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc;
                        }
                    }
                }if (du[u]==2){
                    fi[hu[la]]+=ci*cc*(du[hu[la]]-1)/du[hu[la]];
                    gi[hu[la]][1]+=ci*cc;
                }ci=0.;cc=1.;
                for (la=i,j=(i==1 ? hz : i-1);j!=i;la=j,j=(j==1 ? hz : j-1)){
                    ci+=hv[j];cc/=1.*(du[hu[j]]-1);
                    if (cmp(gi[u][0],0.)>0){
                        if ((j==1 ? hz : j-1)!=i){
                            fi[hu[j]]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*
                                        cc*(du[hu[j]]-1)/du[hu[j]];
                            gi[hu[j]][1]+=(gi[u][0]+ci*(du[u]-2)/(du[u]-1))*cc;
                        }else{
                            fi[hu[j]]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc*(du[hu[j]]-1)/du[hu[j]];
                            gi[hu[j]][1]+=(gi[u][0]*(du[u]-1)/(du[u]-2)+ci)*cc;
                        }
                    }
                }if (du[u]==2){
                    fi[hu[la]]+=ci*cc*(du[hu[la]]-1)/du[hu[la]];
                    gi[hu[la]][1]+=ci*cc;
                }
            }for (i=1;i<=n;++i)
                if (oh[i]){gi[i][0]=gi[i][1];gi[i][1]=0.;}
            for (i=1;i<=n;++i)
                if (oh[i]) gdown(i,0,0.);
            for (i=1;i<=n;++i) ans+=1./n*fi[i];
        }printf("%.5f
    ",ans);
    }
    View Code

    bzoj3242 快餐店

    题目大意:给出一棵环套树,在某个位置(边或点上)放一个快餐店,使得最远点到这个店的距离最小,输出最小距离。

    思路:可以证明方案中一定有一条环上的边不经过(边左右的点分别经过左右到那个快餐店或者快餐店所在外向树的根!!。所以可以删掉一条边之后,求出树的直径/2就是答案,考虑快速求这个直径。如果在外向树上,是不变的,可以dp出一个值mx(这一部分不要忘记!!)。经过环的话,是不相同的两点i、j,距离是max(sm[i]-sm[j]+fi[i]+fi[j]),sm[i]表示不选边的一端为起点的边权和,fi[i]表示i这个点外向树从根出去的最长链,可以维护最大的sm[i]+fi[i],最小的sm[i]-fi[i],因为i和j不能相同,维护出次大次小值,求的时候判断一下。取删掉不同边的直径的最小值。

    注意:1)分清最大最小值:外向树上的直径是要和环上直径取max的;环上求直径的时候,如果最大最小值的i一样,用最大次小和最小次大的较大值更新;

       2)这里的sum只需要求相对差就可以了,不用区间修改,只需要用上一个+边权更新这一个就可以了。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define N 100005
    #define M 200005
    #define LL long long
    #define mid (l+r)/2
    #define lson i<<1,l,mid
    #define rson i<<1|1,mid+1,r
    #define inf 1000000000000000000LL
    using namespace std;
    int in(){
        char ch=getchar();int x=0;
        while(ch<'0'||ch>'9') ch=getchar();
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';ch=getchar();
        }return x;}
    int point[N]={0},next[M],en[M],tot=0,zh[N],zt=0,hu[N],hz,fe[N]={0};
    LL hv[N],va[M],fi[N]={0},dis[N]={0},sm[N]={0},ld=0LL,gi[N]={0};
    bool vi[N]={false},oh[N]={false},f=false;
    void add(int u,int v,LL w){
        next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;
        next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w;
    }
    void geth(int x){
        for (hz=0;;--zt){
            hu[++hz]=zh[zt];
            oh[zh[zt]]=true;
            hv[hz]=va[fe[zh[zt]]];
            if (zh[zt]==x) break;
        }
    }
    void dfs(int u,int fa){
        int i,v;vi[u]=true;zh[++zt]=u;
        for (i=point[u];i;i=next[i]){
            if ((v=en[i])==fa) continue;
            if (vi[v]){geth(v);f=true;hv[hz]=va[i];return;}
            fe[v]=i;
            dfs(v,u);if (f) return;
        }--zt;
    }
    void getf(int u,int anc){
        int i,v;LL mx=0LL,cmx=0LL,vv;
        vi[u]=true;gi[u]=0LL;
        fi[anc]=max(fi[anc],dis[u]);
        for (i=point[u];i;i=next[i]){
            if (vi[v=en[i]]) continue;
            dis[v]=dis[u]+va[i];
            getf(v,anc);vv=gi[v]+va[i];
            gi[u]=max(gi[u],vv);
            if (vv>=mx){cmx=mx;mx=vv;}
            else if (vv>=cmx) cmx=vv;
        }ld=max(ld,mx+cmx);
    }
    struct use{LL mx,mn,cmx,cmn;int px,pn,cpx,cpn;}tr[N<<2];
    use updata(use x,use y){
        if (y.mx>=x.mx){
            x.cmx=x.mx;x.cpx=x.px;
            x.mx=y.mx;x.px=y.px;
        }else if (y.mx>=x.cmx){x.cmx=y.mx;x.cpx=y.px;}
        if (y.cmx>=x.cmx){x.cmx=y.cmx;x.cpx=y.cpx;}
        if (y.mn<=x.mn){
            x.cmn=x.mn;x.cpn=x.pn;
            x.mn=y.mn;x.pn=y.pn;
        }else if (y.mn<=x.cmn){x.cmn=y.mn;x.cpn=y.pn;}
        if (y.cmn<=x.cmn){x.cmn=y.cmn;x.cpn=y.cpn;}
        return x;}
    void build(int i,int l,int r){
        if (l==r){tr[i]=(use){sm[l]+fi[l],sm[l]-fi[l],-inf,inf,l,l,0,0};return;}
        build(lson);build(rson);
        tr[i]=updata(tr[i<<1],tr[i<<1|1]);
    }
    void tch(int i,int l,int r,int x){
        if (l==r){tr[i]=(use){sm[l]+fi[l],sm[l]-fi[l],-inf,inf,l,l,0,0};return;}
        if (x<=mid) tch(lson,x);
        else tch(rson,x);
        tr[i]=updata(tr[i<<1],tr[i<<1|1]);
    }
    int main(){
        int n,i,j,k,u,v,w;LL ans=inf;
        use ci;n=in();
        for (i=1;i<=n;++i){
            u=in();v=in();w=in();
            add(u,v,(LL)w);
        }dfs(1,0);
        for (i=1;i<=n;++i) vi[i]=oh[i];
        for (i=1;i<=hz;++i) getf(hu[i],i);
        for (i=1;i<=hz;++i) sm[i]=sm[i-1]+hv[i-1];
        build(1,1,hz);
        for (i=1;i<=hz;++i){
            if (i>1){
                j=i-1;k=(j==1 ? hz : j-1);
                sm[j]=sm[k]+hv[k];
                tch(1,1,hz,j);
            }ci=tr[1];
            if (ci.px!=ci.pn) ans=min(ans,ci.mx-ci.mn);
            else ans=min(ans,max(ci.mx-ci.cmn,ci.cmx-ci.mn));
        }printf("%.1f
    ",1.*max(ans,ld)/2.);
    }
    View Code
  • 相关阅读:
    clientWidth和offsetWidth区别 e.pageX和e.clientX区别
    vue笔记(七)组件的生命周期
    vue笔记(七)网络封装
    vue笔记(六)自定义消息弹出
    vue笔记(五)插槽. 路由
    组件中的样式问题,穿透和scale占位, 引入静态资源
    python基础-迭代器
    python基础-装饰器
    python基础-函数对象和闭包
    python基础-名称空间与作用域
  • 原文地址:https://www.cnblogs.com/Rivendell/p/4760952.html
Copyright © 2011-2022 走看看