zoukankan      html  css  js  c++  java
  • 【BZOJ2878】[NOI2012] 迷失游乐园(基环树DP)

    点此看题面

    大致题意: 给定一棵树/基环树,随机一点作为起点,且每次随机走向一个没有走过的点,当旁边没有没走过的点时结束行走。每条边有一定边权,求走的总长度的期望。

    基环树

    应该是一道思维难度不高的题目,树的情况显然是非常套路的,而基环树的情况只要多讨论一个环上的转移即可。

    但是,思维难度不高,代码难度却高啊,至少我感觉我的做法非常复杂,调了半个下午才调出来。

    首先,对于环上每一个点,我们先遍历其子树求出一个(f_x)表示(x)子树内的贡献总和。

    然后,考虑在环上走的情况,显然从(i)走到(i+1)的概率是(frac{1}{deg_i-1})(i)不是起点,(i)(i-1)同理),但还要注意如果已经在环上绕了一圈,(i)的下一个点是起点,就不能接着在环上走了。

    所以在这一过程中只要特殊处理这最后一个点,并维护好其他所有点的贡献总和即可。

    至于其他的一些细节问题,由于调得很累,这里就懒得多说了,直接看代码吧。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define DB long double
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,m,ee,lnk[N+5];struct edge {int to,nxt,val;}e[2*N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		#undef D
    }F;
    class CircleTreeDP
    {
    	private:
    		int cnt,c[2*N+5],p[N+5],D[N+5];DB ans,f[N+5],s[N+5];
    		DB sum[2*N+5],F[2*N+5],pre[2*N+5],suf[2*N+5];
    		I int E(CI x,CI y) {for(RI i=lnk[x];i;i=e[i].nxt) if(e[i].to==y) return e[i].val;}
    		int vis[N+5],d[N+5],fa[N+5];I bool Find(CI x,CI lst=0)//找环
    		{
    			vis[x]=1;for(RI i=lnk[x],u,v;i;i=e[i].nxt) if((v=e[i].to)^lst)
    			{
    				if(!vis[v]) {if(d[v]=d[fa[v]=x]+1,Find(v,x)) return 1;continue;}
    				d[u=x]<d[v]&&swap(u,v);W(d[u]^d[v]) p[c[++cnt]=u]=1,u=fa[u];
    				RI w=v,t=0;W(p[c[++cnt]=u]=1,u^v) u=fa[u],v=fa[v],++t;
    				W(t) p[c[cnt+(t--)]=w]=1,w=fa[w];return 1;
    			}return 0;
    		}
    		I void DP(CI x,CI lst=0)//DP求出子树内的贡献
    		{
    			D[x]=1;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&!p[e[i].to]&&
    				(DP(e[i].to,x),f[x]+=(s[e[i].to]+=e[i].val),++D[x]);s[x]=D[x]^1?f[x]/(D[x]-1):0;
    		}
    		I void Calc(CI x,CI lst=0,DB t=0)//结合子树外的贡献统计答案
    		{
    			ans+=(f[x]+t)/D[x];for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&
    				!p[e[i].to]&&(Calc(e[i].to,x,(D[x]^1?(f[x]+t-s[e[i].to])/(D[x]-1):0)+e[i].val),0);
    		}
    	public:
    		I void Tree() {DP(1),--D[1],Calc(1,0),printf("%.5Lf
    ",ans/n);}//处理树的情况
    		I void CTree()//处理基环树的情况
    		{
    			RI i;DB g,u;for(Find(1),i=1;i<=cnt;++i) DP(c[i]),++D[c[cnt+i]=c[i]];//对环上每个点先DP
    			for(pre[0]=i=1;i<=(cnt<<1);++i) pre[i]=pre[i-1]/(D[c[i]]-1);//统计前缀概率积
    			for(suf[cnt<<1|1]=1,i=(cnt<<1);i;--i) suf[i]=suf[i+1]/(D[c[i]]-1);//统计后缀概率积
    			for(F[1]=(D[c[1]]^2?f[c[1]]/(D[c[1]]-2):0),i=2;i<=(cnt<<1);++i)
    				sum[i]=sum[i-1]+E(c[i-1],c[i]),F[i]=(D[c[i]]^2?f[c[i]]/(D[c[i]]-2):0)+sum[i];
    			for(g=0,i=2;i^cnt;++i) g+=pre[i]*(D[c[1]]-1)*(D[c[i]]-2)*F[i];//预处理第1个点的答案
    			for(i=1;i<=cnt;++i) f[c[i]]+=g+pre[i+cnt-2]/pre[i]*F[i+cnt-1]-sum[i],//更新f
    				g=g*(D[c[i+1]]-1)+pre[i+cnt-1]/pre[i+1]*(D[c[i+cnt-1]]-2)*F[i+cnt-1]-(D[c[i+1]]-2)*F[i+1];//更新g
    			for(F[cnt<<1]-=sum[cnt<<1],sum[cnt<<1]=0,i=2*cnt-1;i;--i)
    				F[i]-=sum[i],sum[i]=sum[i+1]+E(c[i],c[i+1]),F[i]+=sum[i];
    			for(g=0,i=2*cnt-1;i^(cnt+1);--i) g+=suf[i]*(D[c[cnt<<1]]-1)*(D[c[i]]-2)*F[i];//预处理第n个点的答案
    			for(i=cnt<<1;i^cnt;--i) f[c[i]]+=g+suf[i-cnt+2]/suf[i]*F[i-cnt+1]-sum[i],//更新f
    				g=g*(D[c[i-1]]-1)+suf[i-cnt+1]/suf[i-1]*(D[c[i-cnt+1]]-2)*F[i-cnt+1]-(D[c[i-1]]-2)*F[i-1];//更新g
    			for(i=1;i<=cnt;++i) Calc(c[i]);printf("%.5Lf
    ",ans/n);//统计答案
    		}
    }T;
    int main()
    {
    	RI i,x,y,z;for(F.read(n,m),i=1;i<=m;++i) F.read(x,y,z),add(x,y,z),add(y,x,z);
    	return m^n?T.Tree():T.CTree(),0;
    }
    
  • 相关阅读:
    如何在Ubuntu Server 18.04上安装Microsoft的Procmon
    如何在Ubuntu 20.04上安装Wine 5.0
    如何在Kali Linux 2020中启用SSH服务
    如何在Ubuntu 20.04 LTS Focal Fossa上安装Apache Groovy
    如何使用命令在Ubuntu 20.04 Linux上安装Vmware Tools
    在Ubuntu 20.04 LTS Focal Fossa上安装Zabbix Agent
    hdu 2089 不要62
    hdu 2093 成绩排名
    hdu 2104 hide handkerchief
    leetcode147对链表进行插入排序
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2878.html
Copyright © 2011-2022 走看看