HDU 5293

树上DP题。

其实有点类似于01的问题。方程很容易想到。首先,因为一条链的节点其实都是在树上的,所以很容易想到应该先求一个LCA。然后,当某节点不是链的LCA时,它的转移就是:

dp[i]=sum[i],其中,sum[i]是i的子节点的dp[i]的和。

如果它是某个点的LCA时,那么它的转移就是dp[i]=val[p]+(sum(sum[k])-sum(dp[k except i])),k是一条chain上的节点。

然而,这样做是不会过的,TLE。。。

树状数组维和chain上的节点的和。QAQ.....

怎么样维护呢?首先,使用DFS把树处理成DFS序,并维护时间戳,这是很显然的,记为l[u],r[u]。然后,假设对于某条链的LCA为u,计算出sum[u]和dp[u]后,对l[u]~r[u]进行区间更新。为什么要区间更新呢?因为,如果以后存在一条chain的起点是在u的子树内,那么,我们在计算链上节点和时,该链必定是经过u点的,这样,求前缀和就可以得到一个和是包含了u点的。区间更新,单点查询。单点查询就是完成了从起或终点到它们的LCA的链上节点求和,因为每次区间更新的时候,因为起点都在子树内,就可以对链上的节点的和不停地累加。

我弱菜没想到优化,TLE的代码。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAX=100010;

struct Point{
	int v,i;
	Point(int vv,int ii){
		v=vv,i=ii;
	}
};

vector<Point>chain[MAX];
vector<int>head_chain[MAX];
//vector<int>chain_point;
int chain_from[MAX],chain_to[MAX],chain_val[MAX];
int pre[MAX];

struct Edge{
	int u,v,next;
}edge[MAX*2];
int head[MAX],tot,n,m;
int sum[MAX],dp[MAX];
int parent[MAX],depth[MAX];
bool color[MAX];

void addedge(int u,int v){
	edge[tot].u=u;
	edge[tot].v=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}

int get_point(int i){
//	chain_point.clear();
	int res=0;
	int u=chain_from[i],v=chain_to[i];
	if(depth[u]<depth[v]) swap(u,v);
	while(depth[u]>depth[v]){
	//	chain_point.push_back(u);
		res+=(sum[u]-dp[u]);
		u=parent[u];
	}
	while(u!=v){
	//	chain_point.push_back(u);
	//	chain_point.push_back(v);
		res+=(sum[u]-dp[u]);
		res+=(sum[v]-dp[v]);
		u=parent[u];
		v=parent[v];
	}
	res+=sum[u];
//	chain_point.push_back(u);
	return res;
}

void dfs(int u,int par){
	sum[u]=0;
	for(int e=head[u];e!=-1;e=edge[e].next){
		int v=edge[e].v;
		if(v!=par){
			dfs(v,u);
			sum[u]+=dp[v];
		}
	}
	dp[u]=sum[u];
	if(head_chain[u].size()){
		int sz=head_chain[u].size();
		int tmp,index,tsz;
		for(int i=0;i<sz;i++){
			index=head_chain[u][i];
			tmp=get_point(index);
		//	tmp=0; tsz=chain_point.size();
		//	for(int k=0;k<tsz;k++){
		//		tmp+=(sum[chain_point[k]]-dp[chain_point[k]]);
		//	}
			dp[u]=max(dp[u],tmp+chain_val[index]);
		}
	}
}

int findx(int u){
	int x=u;
	while(pre[u]!=-1){
		u=pre[u];
	}
	while(pre[x]!=-1){
		int t=pre[x];
		pre[x]=u;
		x=t;
	}
	return u;
}

void DFS(int u,int par,int dep){
	parent[u]=par;
	depth[u]=dep;
	for(int e=head[u];e!=-1;e=edge[e].next){
		int v=edge[e].v;
		if(v!=par){
			DFS(v,u,dep+1);
			pre[findx(v)]=u;
		}
	}
	color[u]=true;
	int sz=chain[u].size(),v;
	for(int i=0;i<sz;i++){
		v=chain[u][i].v;
		if(color[v]){
			head_chain[findx(v)].push_back(chain[u][i].i);
		}
	}
}

/*
void get_head(int i){
	int u=chain_from[i],v=chain_to[i];
	if(depth[u]<depth[v]) swap(u,v);
	while(depth[u]>depth[v]){
		u=parent[u];
	}
	while(u!=v){
		u=parent[u],v=parent[v];
	}
	head_chain[u].push_back(i);
}
*/
int main(){
	int T,u,v;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		tot=0;
		memset(head,-1,sizeof(head));
		memset(pre,-1,sizeof(pre));
		memset(color,false,sizeof(color));
		for(int i=1;i<n;i++){
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
			head_chain[i].clear();
			chain[i].clear();
		}
		chain[n].clear();
		head_chain[n].clear();
		for(int i=1;i<=m;i++){
			scanf("%d%d%d",&chain_from[i],&chain_to[i],&chain_val[i]);
			chain[chain_from[i]].push_back(Point(chain_to[i],i));
			chain[chain_to[i]].push_back(Point(chain_from[i],i));
		//	get_head(i);
		}
		DFS(1,1,1);
		dfs(1,0);
		printf("%d\n",dp[1]);
	}
	return 0;
}

http://blog.csdn.net/qq_24451605/article/details/47003497

简单的代码,其实写起来真的不难,主要是优化没想到。。。。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define MAX 200007

using namespace std;

int n,m,t;

typedef long long LL;

LL d[MAX];
LL sum[MAX];

LL c1[MAX<<1];
LL c2[MAX<<1];

int lowbit ( int x )
{
    return x&-x;
}

void add1 ( int x , LL v )
{
    while ( x <= n )
    {
        c1[x] += v;
        x += lowbit ( x );
    }
}

void add2 ( int x , LL v )
{
    while ( x <= n )
    {
        c2[x] += v;
        x += lowbit ( x );
    }
}

LL sum1 ( int x )
{
    LL res = 0;
    while ( x )
    {
        res += c1[x];
        x -= lowbit ( x );
    }
    return res;
}

LL sum2 ( int x )
{
    LL res = 0;
    while ( x )
    {
        res += c2[x];
        x -= lowbit ( x );
    }
    return res;
}

typedef pair<int,int> PII;

vector<int> e[MAX];
vector<int> chain[MAX];
vector<PII> a[MAX];
vector<LL> w[MAX];
vector<LL> val[MAX];

int fa[MAX];
int times;
bool used[MAX];
int l[MAX];
int r[MAX];

int _find ( int x )
{
    return fa[x] == x ? x: fa[x] = _find ( fa[x]);
}

void LCA ( int u )
{
    fa[u] = u;
    l[u] = ++times;
    used[u] = true;
    for ( int i = 0 ; i < e[u].size() ; i++ )
    {
        int v = e[u][i];
        if ( used[v] ) continue;
        LCA ( v );
        fa[v] = u;
    }
    for ( int i = 0 ; i < chain[u].size() ; i++ )
    {
        int v = chain[u][i];
        if ( !used[v] ) continue;
        int x = _find ( v );
        a[x].push_back ( make_pair ( u , v ));
        w[x].push_back ( val[u][i] );
    }
    r[u] = ++times;
}

void dfs ( int u , int p )
{
    sum[u] = 0;
    d[u] = 0;
    for ( int i = 0 ; i < e[u].size() ; i++ )
    {
        int v = e[u][i];
        if ( v == p ) continue;
        dfs ( v , u );
        sum[u] += d[v];
    }
    for ( int i = 0 ; i < a[u].size() ; i++ )
    {
        int x = a[u][i].first;
        int y = a[u][i].second;
        LL temp = sum1(l[x]) + sum1(l[y]) + sum[u]
                -sum2(l[x]) - sum2(l[y]);
        d[u] = max ( temp + w[u][i] , d[u] );
    }
    d[u] = max ( d[u] , sum[u] );
    add1 ( l[u] , sum[u] );
    add1 ( r[u] , -sum[u] );
    add2 ( l[u] , d[u] );
    add2 ( r[u] , -d[u] );
}

void init ( )
{
    times = 0;
    memset ( c1 , 0 , sizeof ( c1 ) );
    memset ( c2 , 0 , sizeof ( c2 ));
    memset ( used , 0 , sizeof ( used ));
    for ( int i = 0 ; i < MAX ; i++ )
    {
        e[i].clear();
        val[i].clear();
        a[i].clear();
        w[i].clear();
        chain[i].clear();
    }
}

int main ( )
{
    int u,v,x;
    scanf ( "%d" , &t );
    while ( t-- )
    {
        init();
        scanf ( "%d%d" , &n , &m );
        int nn = n-1;
        while ( nn-- )
        {
            scanf ( "%d%d" , &u , &v );
            e[u].push_back ( v );
            e[v].push_back ( u );
        }
        n = n*2;
        while ( m-- )
        {
            scanf ( "%d%d%d" , &u , &v , &x );
            chain[u].push_back ( v );
            chain[v].push_back ( u );
            val[u].push_back ( x );
            val[v].push_back ( x );
        }
        //cout <<"YES" << endl;
        LCA ( 1 );
        dfs ( 1, -1 );
        printf ( "%I64d\n" , d[1] );
    }
}
时间: 2024-10-19 04:17:09

HDU 5293的相关文章

hdu 5293 Tree chain problem(树链剖分+树形dp)

题目链接:hdu 5293 Tree chain problem 维护dp[u], sum[u],dp[u]表示以u为根节点的子树的最优值.sum[u]表示以u节点的所有子节点的dp[v]之和.对于边a,b,w,在LCA(a,b)节点的时候进行考虑.dp[u] = min{dp[u], Sum(a,b) - Dp(a,b) + sum[u] | (ab链上的点,不包括u } #pragma comment(linker, "/STACK:1024000000,1024000000")

HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形dp, 对于每条链u,v,w,我们只在lca(u,v)的顶点上处理它 让dp[i]表示以i为根的指数的最大值,sum[i]表示dp[vi]的和(vi为i的儿子们) 则i点有两种决策,一种是不选以i为lca的链,则dp[i]=sum[i]. 另一种是选一条以i为lca的链,那么有转移方程:dp[i]=

HDU 5293(Tree chain problem-树链剖分)

Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 940    Accepted Submission(s): 248 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,-,n. Th

(中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are m chain on the tree, Each chain has a certain weight. Coco would like to pick out some chains any two of which do not share common vertices.Find out the

HDU 5293 Tree chain problem

Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 565    Accepted Submission(s): 137 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,-,n. Th

HDU 5293 TREE CHAIN PROBLEM LCT+树形DP

题解链接 代码链接 链接 Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 35    Accepted Submission(s): 8 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1

2015 ACM多校训练第一场

在下面网址看效果更佳>_< http://mlz000.github.io/2015/08/07/2015-ACM%E5%A4%9A%E6%A0%A1%E8%AE%AD%E7%BB%83%E7%AC%AC%E4%B8%80%E5%9C%BA/ 题外话 这个暑假以前就决定要把这次多校的所有题全补了,中间断断续续,总算把第一场的题补全了,鄙视一下颓废的自己... hdu 5288(1001) OO's Sequence Solution 水题,定义两个数组L[i],R[i]示第i个数左侧和右侧最接

[2015hdu多校联赛补题]hdu5293 Tree chain problem

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意:给你一棵n个点的树,和一些该树上的树链,每个树链有一定的权值,要求选出一些不相交的树链,使他们的权值和尽量大 解: 树形dp,dp[x]表示以x为根节点的子树的解,那么就可以基于在当前结点对树链的选和不选进行dp 现在有两个问题: 1.在树链上的哪个点进行选择? 2.选和不选分别如何操作? 对于第一个问题,粗略想一下(在解决问题二的时候会发现),树链上最浅的一个点(也就是树链两端的lca

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;