zoukankan      html  css  js  c++  java
  • 洛谷 P2634 [国家集训队]聪聪可可 树形DP 题解

    这是我第一篇用markdown写的博客,格式不好请见谅。

    每日一题 day63 打卡

    Analysis

    这道题正解是点分治,但我发现了树形DP的做法,于是我就写了树形DP。

    [dp[i][0/1/2]表示i的子树中有多少个点与i距离模3余数为0,1,2 ]

    首先思考如何转移,对于每个i来说,可以用dp[from][(j+val)%3]+=dp[to][j] (j为枚举的情况)来更新根节点的答案,因为每一个子树中的距离在新的根中都会+val

    接下来思考如何记录答案,用乘法原理计算当前子树的状态与之前算过的所有子树的状态会形成多少合法解,即ans+=dp[to][j]( imes) dp[from][((-j-val)%3+3)%3]( imes) 2 。这时,作为读者的你一定会问,为什么是((-j-val)%3+3)%3? 因为是当前子树的状态与之前算过的所有子树的状态且要满足(x+y)%3=0,所以应该是3-j-val3可以约掉,(j>0&val>0) ,得出((-j-val)%3+3)%3

    注意事项:

    1.因为有可能只有一个子树,所以将dp[from][0]=1来避免这种情况。

    2.因为数对(两个人选的点)有序,所以在计算答案时( imes 2)

    3.因为两个点重合时答案合法,所以最后要将(ans+=n)

    4.输出时别忘了约分。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define int long long
    #define maxn 20000+10
    #define rep(i,s,e) for(register int i=s;i<=e;++i)
    #define dwn(i,s,e) for(register int i=s;i>=e;--i)
    using namespace std;
    inline int read()
    {
    	int x=0,f=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    	while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
    	return f*x;
    }
    void write(int x)
    {
    	if(x<0) {putchar('-'); x=-x;}
    	if(x>9) write(x/10);
    	putchar(x%10+'0');
    }
    int n,cnt,ans;
    int head[maxn];
    int dp[maxn][3];
    struct node
    {
    	int v,w,nex;
    }edge[2*maxn];
    inline void add(int x,int y,int z)
    {
    	edge[++cnt].v=y;
    	edge[cnt].w=z;
    	edge[cnt].nex=head[x];
    	head[x]=cnt;
    }
    void tree_dp(int from,int father)
    {
    	dp[from][0]=1;
    	for(int i=head[from];i;i=edge[i].nex)
    	{
    		int to=edge[i].v,val=edge[i].w%3;
    		if(to==father) continue;
    		tree_dp(to,from);
    		rep(j,0,2)
    			ans+=dp[to][j]*dp[from][((-j-val)%3+3)%3]*2;
    		rep(j,0,2)
    			dp[from][(j+val)%3]+=dp[to][j];
    	}
    }
    signed main()
    {
    	n=read();
    	rep(i,1,n-1)
    	{
    		int x=read(),y=read(),z=read();
    		add(x,y,z);
    		add(y,x,z);
    	}
    	tree_dp(1,-1);
    	ans+=n;
    	int fz=ans,fm=n*n;
    	write(fz/__gcd(fz,fm));
    	putchar('/');
    	write(fm/__gcd(fz,fm));
    	return 0;
    }
    
    

    如有失误请各位大佬斧正(反正我不认识斧正是什么意思)

  • 相关阅读:
    [转]Asp.Net 备份和恢复SQL SERVER 数据库
    alert 的封装
    using(sqlConnection conn=new sqlConnection) 中using的作用
    dotnet 上传大文件的配置的方法
    allowDefinition='MachineToApplication'
    转 Server Application Error报错信息的解决方案
    url 自动加入链接
    MVC中使用RadioButtonFor
    linux iptables squid 透明代理
    linux iptables网关配置,端口转发
  • 原文地址:https://www.cnblogs.com/handsome-zyc/p/12111463.html
Copyright © 2011-2022 走看看