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; }
相关文章推荐
- Android的Http通信加载页面、下载图片 以及doGet、doPost请求服务器
- POJ 3463 Sightseeing
- hdu 1325
- hdu 1247 Hat’s Words 字典树
- iOS开发分分钟搞定C语言—— 字符串和指针
- Memento模式
- HDU-5340 Three Palindromes(字符串哈希)
- MySQL 5.6.13 解压版(zip版)安装配置方法
- Handler的使用
- UVALive 6041 Retrenchment(KD树 + 计算几何)
- POJ 2231 Moo Volume
- c++ 11 类成员初始化
- [LeetCode] Reverse Nodes in k-Group
- 线性表的相关基础概念
- 触动精灵远程Log模块
- 比较专家与非专家如何安全上网
- Linux下如何避免僵尸进程的产生
- 安装vs语言包时出现windows 程序兼容模式已打开.请将其关闭
- 掌上快递 APP 项目之概述篇
- POJ 3416 Crossing