hdu5325 多校第三场1010


题目描述:

给出一棵树,n~5e5, 每个点都有一个权值.找出点个数最多的这样的连. 链的要求是(1)链本身是联通的,中间不能够隔着没有选的点.(2)如果按照权值大小排序,我们看每一对相邻权值的点,ij,wi

题解:

主要是一道性质题.
解法一:树形dp的思维. 因为保证了连通性,所以可以这样做.我们看当前的小根节点u. 以u为中心向它的儿子扩展.儿子v. 下面是一个重要的性质:一个u连接的两个儿子v1, v2,如果v1和v2都比u小,那么一定不行. 如果是两个都比u大,或者只有一个小的就行. 那么我们dp状态要记录,u下面的一层v,是否取过比u小的,或者全部都是比u大的. 状态转移: dp[u][0]表示下面一层v都比u大,dp[u][1],表示u下面一层有一个且仅有一个比u小. dp[u][0]枚举所有的比u大的v,并且只能从dp[v][0] 转移过来. 而dp[u][1]其实就是dp[u][0]+tmp, tmp是 比u小的儿子中所有dp[v][0/1]的最大值.这样就转移过来了.
重点是发现了u的两端只有两个都是小于的才会不合法. 并且u的转移只会引起u和v的不合法.
解法二:题解的思路.更加直接.看最优链的最小值.枚举最小值的话,那么流向它的所有的递减的链都是满足的.那么从他递增的链满足吗?如果只有一个,是满足的,有两个就不满足了. 这种情况说明他并不是最小的,我们应该枚举它能够流到的更小的值作为最小值. 具体写法可以统计入度然后遍历一次.之前的建立wi>wj的有向边.

重点:

法一:观察出不合法的结构,用dp来数合法结构中个数最多的.
法二:根据性质贪心的找合法的最好的.并且枚举强制限定最小值也是很重要的思路.

代码:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const int maxn = 500000+100;
int dp[maxn][2];
int n;
int w[maxn];
vector<int> G[maxn];

void dfs(int u, int fa)//树形dp.主要是转移
{
    dp[u][0] = 1;
    dp[u][1] = 0;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            dfs(v, u);
        }
    }
    int t = 0;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            if(w[v] > w[u])
            {
              t += dp[v][0];
            }
        }
    }
    t++;
    dp[u][0] = t;
    int a = t;
    REP(i, 0, G[u].size())
    {
        int v = G[u][i];
        if(v!=fa)
        {
            if(w[v]<w[u])
            {
                int tt = max(dp[v][0], dp[v][1]);
                if(t + tt>a)
                {
                    a = t+tt;
                }
            }
        }
    }
    dp[u][1] = a;
}

void solve()
{
    dfs(1, 0);
    int ans = 0;
    REP_D(i, 1, n)
    {
        //printf("u is %d  000 is %d  111 is %d\n", i, dp[i][0], dp[i][1]);
        ans = max(dp[i][0], ans);
        ans = max(dp[i][1], ans);
    }
    printf("%d\n", ans);
}

int main()
{
   // freopen("10Jin.txt", "r", stdin);
    //freopen("10Jout.txt", "w", stdout);
    while(scanf("%d", &n) != EOF)
    {
        REP_D(i, 1, n)
        {
            scanf("%d", &w[i]);
            G[i].clear();
        }
        REP_D(i, 1, n-1)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值