[Codeforces 23E] Tree

Brief Intro:

一棵树,删去若干条边,最大化得到的所有连通块大小的乘积

Algorithm:

这其实算是一类题型吧,虽然这是我做的第一题

树形DP,维护关于子树根节点的信息

此处用dp[i][s],表示以i为根的子树,且i所属连通块的大小为s时的最大值

转移时还是树形DP的常规套路,用类似背包的形式转移:dp[i的孩子][k]*dp[i][s]  ------->   dp[i][k+s]

需要注意的是,此题需要高精度,写一个Big Integer模板类即可

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

template<class T> inline void putnum(T x)
{
    if(x<0)putchar(‘-‘),x=-x;
    register short a[20]={},sz=0;
    while(x)a[sz++]=x%10,x/=10;
    if(sz==0)putchar(‘0‘);
    for(int i=sz-1;i>=0;i--)putchar(‘0‘+a[i]);
}

inline ll read()
{
    char ch;ll num,f=0;
    while(!isdigit(ch=getchar())) f|=(ch==‘-‘);
    num=ch-‘0‘;
    while(isdigit(ch=getchar())) num=num*10+ch-‘0‘;
    return f?-num:num;
}

const int MAXN=705;
const int MAXLEN=120;

struct BI  模板类
{
    int d[MAXLEN],len;

    BI(){memset(d,0,sizeof(d)),len=1;}
    BI(int num){*this=num;}

    BI& operator = (const int& num)
    {
        memset(d,0,sizeof(d));
        int t=num;len=0;
        while(t) d[++len]=t%10,t/=10;
        return *this;
    }

    void clear()
    {
        while(len>1 && !d[len]) len--;
    }

    BI operator + (const BI& num)
    {
        BI ret=*this;
        ret.len=max(num.len,len);
        for(int i=1;i<=ret.len;i++)
        {
            ret.d[i]+=num.d[i];
            if(ret.d[i]>=10) ret.d[i]-=10,ret.d[i+1]++;
        }
        if(ret.d[ret.len+1]) ret.len++;
        return ret;
    }

    BI operator * (const BI& num) const
    {
        BI ret;
        for(int i=1;i<=len;i++)
            for(int j=1;j<=num.len;j++)
                ret.d[i+j-1]+=d[i]*num.d[j];
        for(int i=1;i<=len+num.len;i++)
            ret.d[i+1]+=ret.d[i]/10,ret.d[i]%=10;
        ret.len=len+num.len+1;
        ret.clear();
        return ret;
    }

    bool operator > (const BI& num)
    {
        if(num.len!=len) return len>num.len;
        for(int i=len;i>=1;i--)     //注意,比较是从后往前比
            if(d[i]!=num.d[i]) return d[i]>num.d[i];
        return false;
    }

    void print()
    {
        for(int i=len;i>=1;i--) putnum(d[i]);
    }
}dp[MAXN][MAXN];

int n,sz[MAXN];
vector<int> G[MAXN];

void dfs(int u,int anc)
{
    sz[u]=1;dp[u][1]=1;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==anc) continue;
        dfs(v,u);

        for(int Nu=sz[u];Nu>=1;Nu--)
            for(int Nv=sz[v];Nv>=0;Nv--)
            {
                BI t=dp[u][Nu]*dp[v][Nv];
                if(t>dp[u][Nu+Nv]) dp[u][Nu+Nv]=t;
            }
        sz[u]+=sz[v];  //size在转移后再更新
    }

    for(int i=1;i<=sz[u];i++)
    {
        BI t=dp[u][i]*BI(i);
        if(t>dp[u][0]) dp[u][0]=t;  //用0号存储最值
    }
}

int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        G[x].push_back(y);G[y].push_back(x);
    }
    dfs(1,0);
    dp[1][0].print();
    return 0;
}

Review:

1、对连续的乘法要敏感,查看是否需要高精度

2、BI重载>号时,要从后往前判断

原文地址:https://www.cnblogs.com/newera/p/9080064.html

时间: 2024-10-25 11:02:05

[Codeforces 23E] Tree的相关文章

Codeforces 23E Tree(树型DP)

题目链接 Tree dp[x][i]表示以x为根的子树中x所属的连通快大小为i的时候 答案最大值 用dp[x][j] * dp[y][k] 来更新dp[x][j + k]. (听高手说这类题的套路其实都差不多) 因为这题输出数据会很大所以用Java-- QAQ 1 import java.util.*; 2 import java.io.*; 3 import java.math.*; 4 5 public class Main{ 6 static final int maxn = 710; 7

CodeForces 23E ,77C,455A,437E,245H

五天每天刷了一dp CodeForces 455A Boredom   定义 :dp[i]为取前i个元素后得到的最大值.则dp[i]=max(dp[i-1],dp[i-2]+a[i]*i); 写的时候愚蠢的分类讨论i元素是否选取.实际上第i-2个元素是否选取和状态dp[i]无关 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define ll __int64 co

Codeforces 570D TREE REQUESTS dfs序+树状数组 异或

http://codeforces.com/problemset/problem/570/D Tree Requests time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Roman planted a tree consisting of?n?vertices. Each vertex contains a lowercase

Codeforces 570D Tree Requests(Dsu On the Tree)

题目链接 Tree Requests 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 typedef long long LL; 8 9 const int N = 500010; 10 const int A = 31; 11 12 int cntf[N], sz[N], h[N]; 13 vector <i

Codeforces 570D TREE REQUESTS dfs序+树状数组

链接 题解链接:点击打开链接 题意: 给定n个点的树,m个询问 下面n-1个数给出每个点的父节点,1是root 每个点有一个字母 下面n个小写字母给出每个点的字母. 下面m行给出询问: 询问形如 (u, deep) 问u点的子树中,距离根的深度为deep的所有点的字母能否在任意排列后组成回文串,能输出Yes. 思路:dfs序,给点重新标号,dfs进入u点的时间戳记为l[u], 离开的时间戳记为r[u], 这样对于某个点u,他的子树节点对应区间都在区间 [l[u], r[u]]内. 把距离根深度相

Codeforces 375D Tree and Queries(DFS序+莫队+树状数组)

题目链接  Tree and Queries 题目大意  给出一棵树和每个节点的颜色.每次询问vj, kj 你需要回答在以vj为根的子树中满足条件的的颜色数目, 条件:具有该颜色的节点数量至少为kj. (莫队居然可以过) 首先转DFS序,这样就变成了区间查询. 然后直接套用莫队,求出每次询问状态下的t[],t[k]表示当前区间内拥有k个节点的颜色数量. 然后统计t[k] + t[k + 1], ..., t[MAX]即可,这个过程用树状数组维护. #include <bits/stdc++.h>

Codeforces 911F Tree Destruction(贪心 &amp;&amp; 树的直径)

题目链接  Tree Destructi 题意  给定一棵树,每次可以选定树上的两个叶子,并删去其中的一个.答案每次加上两个选定的叶子之间的距离. 求最后答案的最大值. 首先求出树的某一条直径,令其端点分别为L, R. 把L看成树的根,那么R一定是叶子结点. 对于那些非直径上的点,离他们最远的点肯定是L或R中的一个(可能也有其他的,但是L或R肯定已经最大了) 所以依次把这些非直径上的点删掉,删掉的时候在L和R中选择一个就行了. 最后把直径删掉即可. 时间复杂度$O(nlogn)$  (应该是可以

Codeforces 932D - Tree

932D - Tree 思路: 树上倍增 anc[i][u]:u的2^i祖先 mx[i][u]:u到它的2^i祖先之间的最大值,不包括u pre[i][u]:以u开始的递增序列的2^i祖先 sum[i][u]:以u开始递增序列从u到2^i祖先的和,不包括u 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mem(a,b) memset(a,b,s

CodeForces 375D Tree and Queries 莫队||DFS序

Tree and Queries 题意:有一颗以1号节点为根的树,每一个节点有一个自己的颜色,求出节点v的子数上颜色出现次数>=k的颜色种类. 题解:使用莫队处理这个问题,将树转变成DFS序区间,然后就是开一个数据记录下出现次数为k次的颜色数目,查询的时候直接返回这个数组中的对应的值就行了. 注意的就是记得将节点的颜色传递给dfs序对应位置的颜色. 这个忘记了找了好久的bug. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3