您的位置:首页 > 其它

hdu5325 多校第三场1010

2015-08-01 23:14 232 查看

题目描述:

给出一棵树,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: