zoukankan      html  css  js  c++  java
  • 不定根问题专题

    不定根问题专题

    此类问题多用树形DP来解决,先指定一个根,快速求出其答案,然后再将当前已算出的答案重复利用,(O(1))的向子节点转移,求出以子节点为根的答案。


    T1 [Coci2015]Kamp

    因为举行聚会的地点不确定,所以假设在(x)点举行,容易算得从(x)(k)个人送回家的最小时间为(x)到每个点的距离的(2)倍-到最远的关键点的距离。

    这个答案通过(dp)很好求,那么转移根的时候,考虑到需要维护最远距离,所以需要保存下当前点到子树中关键点的最长距离和次长距离,以及最长距离的转移点,这样就可以做到轻松转移了,详情看代码:

    #include<bits/stdc++.h>
    #define N 600000
    #define M 1010000
    #define ll long long
    using namespace std;
    const ll inf=1e16;
    int n,k,head[N],cnt=1,book[N];
    struct note{
    	int to,nxt;
    	ll w;
    }a[M];
    void add(int x,int y,ll z)
    {
    	a[cnt].to=y;
    	a[cnt].w=z;
    	a[cnt].nxt=head[x];
    	head[x]=cnt++;
    }
    ll f[N],sum[N],mx[N][2];
    ll md[N],sz[N],nxt[N],ans,st[N],top;
    void dfs(int x,int fa)
    {
    	int i;
    	f[x]=fa,mx[x][0]=mx[x][1]=md[x]=-inf;
    	if(book[x]) mx[x][0]=0,sz[x]=1;
    	st[++top]=x;
    	for(i=head[x];i;i=a[i].nxt)
    	{
    		int to=a[i].to;
    		if(to==fa) continue;
    		dfs(to,x);
    		sz[x]+=sz[to];
    		if(!sz[to]) continue;
    	    sum[1]+=a[i].w;
    		if(mx[to][0]+a[i].w>=mx[x][0])
    	    {
    	    	nxt[x]=to;
    	    	mx[x][1]=mx[x][0];
    	    	mx[x][0]=mx[to][0]+a[i].w;
    	    }
    	    else if(mx[to][0]+a[i].w>mx[x][1])
    	    	mx[x][1]=mx[to][0]+a[i].w;
    	}
    }
    int main()
    {
    	int i,j,x,u,v;
    	ll w;
    	scanf("%d%d",&n,&k);
    	for(i=1;i<n;i++)
    	{
    		scanf("%d%d%lld",&u,&v,&w);
    		add(u,v,w);add(v,u,w);
    	}
    	for(i=1;i<=k;i++)
    	{
    		scanf("%d",&x);
    		book[x]=1;
    	}
    	dfs(1,0);
    	for(i=1;i<=n;i++)
    	{
    		ll x=st[i];
    		for(j=head[x];j;j=a[j].nxt){
    			int y=a[j].to,z=a[j].w;
    			if(y==f[x])continue;
    			if(sz[y]==k) sum[y]=sum[x]-z;
    			else if(sz[y]==0) sum[y]=sum[x]+z;
    			else sum[y]=sum[x];
    			if(nxt[x]==y)md[y]=max(md[x],mx[x][1])+z;
    			else md[y]=max(md[x],mx[x][0])+z;
    		}
    	}
    	for(i=1;i<=n;i++){
    		printf("%lld
    ",sum[i]*2-max(md[i],mx[i][0]));
    	}
    	return 0;
    }
    

    T2 [CQOI2009]叶子的染色

    首先可以证明(猜)出一个结论,以任意一个非叶子节点为根的答案是一样的。

    所以我们随意选择一个节点为根,每一个节点染的颜色值域它的儿子节点有关,所以设状态为(dp[x][0/1])为当前节点染黑色或白色时,当前节点子树中着色节点个数最小值。

    易得转移方程:

    [dp[x][0]=sum_{to}min(dp[to][0]-1,dp[to][1]) ]

    [dp[x][1]=sum_{to}min(dp[to][1]-1,dp[to][0]) ]

    #include<bits/stdc++.h>
    #define N 600000
    #define M 1010000
    #define ll long long
    using namespace std;
    int n,m,c[N],head[N],cnt=1;
    int dp[N][2],inf=1e9;
    struct note{
    	int to,nxt;
    }a[M];
    void add(int x,int y)
    {
    	a[cnt].to=y;
    	a[cnt].nxt=head[x]; 
    	head[x]=cnt++;
    }
    void dfs(int x,int fa)
    {
    	int i;
    	if(x<=n)
    	{
    		dp[x][c[x]]=1;
    		dp[x][!c[x]]=inf;
    		return;
    	}
    	dp[x][1]=dp[x][0]=1;
    	for(i=head[x];i;i=a[i].nxt)
    	{
    		int to=a[i].to;
    		if(to==fa) continue;
    		dfs(to,x);
    		dp[x][0]+=min(dp[to][1],dp[to][0]-1);
    		dp[x][1]+=min(dp[to][0],dp[to][1]-1); 
    	}
    }
    int main()
    {
    	int i,u,v;
    	scanf("%d%d",&m,&n);
    	for(i=1;i<=n;i++) scanf("%d",&c[i]);
    	for(i=1;i<m;i++)
    	{
    		scanf("%d%d",&u,&v);
    		add(u,v),add(v,u);
    	}
    	dfs(n+1,0);
    	printf("%d
    ",min(dp[n+1][0],dp[n+1][1]));
    	return 0;
    }
    

    T3 [SHOI2014]概率充电器

    一道概率水题。

    首先每个元件充电个数的期望,等价于每个元件充电概率之和。

    于是问题变成了求每个元件充电的概率,即(1-未充电的概率)

    于是定根,列转移方程,然后按套路换根转移即可。

    #include<bits/stdc++.h>
    #define N 600000
    #define M 1010000
    #define ll long long
    using namespace std;
    int n,head[N],cnt=1;
    struct note{
    	int to,nxt;
    	double w;
    }a[M];
    void add(int x,int y,double z)
    {
    	a[cnt].to=y;
    	a[cnt].w=z;
    	a[cnt].nxt=head[x];
    	head[x]=cnt++;
    }
    double dp[N],ans[N],Ans,p[N];
    void dfs(int x,int fa)
    {
    	int i,j,pd=0;
    	dp[x]=1-p[x];
    	for(i=head[x];i;i=a[i].nxt)
    	{
    		int to=a[i].to;
    		if(to==fa) continue;
    	    dfs(to,x);
    	    dp[x]*=(1-a[i].w+a[i].w*dp[to]);
    	}
    }
    void dfs2(int x,int fa,int e)
    {
    	if(x==1) ans[x]=dp[x];
    	else
    	{
    		double P=ans[fa]/(1-a[e].w+a[e].w*dp[x]);
    		ans[x]=dp[x]*(1-a[e].w+a[e].w*P);
    	}
    	for(int i=head[x];i;i=a[i].nxt)
    	{
    		int v=a[i].to;
    		if(v==fa)continue;
    		dfs2(v,x,i);
    	}
    }
    int main()
    {
    	int i,j,u,v,w;
    	scanf("%d",&n);
    	for(i=1;i<n;i++)
    	{
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,1.0*w/100);
    		add(v,u,1.0*w/100);
    	}
    	for(i=1;i<=n;i++) 
    	{
    		scanf("%lf",&p[i]); 
    		p[i]/=100;
    	}
    	dfs(1,0);ans[1]=dp[1];
    	dfs2(1,0,0);
    	for(i=1;i<=n;i++)
    	{
    		Ans+=1-ans[i];
    	}
    	printf("%.6lf
    ",Ans);
    	return 0;
    }
    
  • 相关阅读:
    我说AOP(面向切面编程)--藏在苹果里的五角星
    mysql workbench 一个‘愚蠢’的设计
    .Net MVC Json 日期格式
    es6 import
    asp.net mvc 模型绑定太糙淡了
    asp.net mvc 报错 CS1617: Invalid option ‘6’ for /langversion; must be ISO-1, ISO-2, 3, 4, 5 or Default
    撸代码时到底用var好还是强类型变量好
    iphone5 从ios7升级到最新9.2
    修复win7 只有IE64 能上网 其他浏览器及应用都无法联网
    使用Teleri 导出实体类数组到Excel
  • 原文地址:https://www.cnblogs.com/yzxx/p/13901307.html
Copyright © 2011-2022 走看看