环套树

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\n",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\n",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\n",1.*max(ans,ld)/2.);
}
View Code

 

转载于:https://www.cnblogs.com/Rivendell/p/4760952.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值