zoukankan      html  css  js  c++  java
  • SPOJ-FTOUR2 Free tour II

    After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.

    The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative (if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form a tree structure. We will choose two places as the departure and destination of the tour.
    
    Since September is the festival season of local inhabitants, some places are extremely crowded (we call them crowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.
    
    Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don't need to be different.
    Input
    
    There is exactly one case. First one line, containing 3 integers N K M, with 1 <= N <= 200000, 0 <= K <= M, 0 <= M <= N.
    
    Next M lines, each line includes an id number of a crowded place.
    
    The last (N - 1) lines describe (N - 1) two-way roads connected N places, form a b i, with a, b is the id of 2 places, and i is its interest value (-10000 <= i <= 10000).
    Output
    
    Only one number, the maximum total interest value we can obtain.
    Example
    
    Input:
    8 2 3
    3
    5
    7
    1 3 1
    2 3 10
    3 4 -2
    4 5 -1
    5 7 6
    5 6 5
    4 8 3
    
    
    Output:
    12
    
    Explanation
    
    We choose 2 and 6 as the departure and destination place, so the tour will be 2 -> 3 -> 4 -> 5 -> 6, total interest value = 10 + (-2) + (-1) + 5 = 12
    * Added some unofficial cases
    

    题目大意

    n个节点带权树,每个点有黑白两种颜色。询问一条路径,使得黑色点数小于等于k且路径长度最大。(nleq 200000)

    题解

    点分,对于每一层:
    (f[i])表示颜色数小于等于i的最大路径,(num[i])表示点i到根的黑点个数(不包括根,如果根是黑色,k-1即可),当遇到节点(u)时,用(f[k-num[i]]+dis[i])更新。
    下面考虑如何维护(f[i]),如果每一次都从(num[x])搞到(k),肯定爆炸,不如考虑每棵子树记录一个(ma[i]),表示子树(num[x])值的最大值,然后从(1)更新到(ma[i]),然后做点(u)的时候,如果(num[u] + ma[i] <= k),拿(f[ma[i]] + dis[u])更新一下答案就行了。

    不难发现,拿来额外更新的值应该取所有已经遍历过的子树的(ma[i])值的最大值。

    但是这样做最坏情况还是(n^2)

    排个序,从小到大。

    变成了各个子树(ma[i])相加,一定是小于n的。
    复杂度变为(O(n)),排序可以O(n)做但我偷懒用了(sort)

    于是总复杂度(O(nlog n))(但我偷懒让复杂度退化成上界很难取到的(O(nlog ^2n)))

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <map>
    #include <cmath>
    inline int max(int a, int b){return a > b ? a : b;}
    inline int min(int a, int b){return a < b ? a : b;}
    inline void swap(int &x, int &y){int  tmp = x;x = y;y = tmp;}
    inline void read(int &x)
    {
        x = 0;char ch = getchar(), c = ch;
        while(ch < '0' || ch > '9') c = ch, ch = getchar();
        while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
        if(c == '-') x = -x;
    }
    const int INF = 0x3f3f3f3f;
    const int MAXN = 200000 + 10;
    struct Edge
    {
    	int u,v,w,nxt;
    	Edge(int _u, int _v, int _w, int _nxt){u = _u, v = _v, w = _w, nxt = _nxt;}
    	Edge(){}
    }edge[MAXN << 1];
    int head[MAXN], cnt, root, sum, w[MAXN], hao[MAXN], a[MAXN], ma[MAXN], ans, tot, vis[MAXN], size[MAXN], f[MAXN], dis[MAXN], num[MAXN], dp[MAXN], tmp, tag[MAXN], n, k, m;
    inline void insert(int a, int b, int c)
    {
    	edge[++ cnt] = Edge(a, b, c, head[a]), head[a] = cnt;
    }
    int cmp(int c, int b)
    {
    	return ma[a[c]] < ma[a[b]];
    }
    void dfs_dp(int x, int pre)
    {
    	size[x] = 1, dp[x] = 0;
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(pre == v || vis[v]) continue;
    		dfs_dp(v, x), size[x] += size[v], dp[x] = max(dp[x], size[v]);
    	}
    	dp[x] = max(dp[x], sum - size[x]);
    	if(dp[x] < dp[root]) root = x;
    }
    //f[i]表示黑点数小于等于i的最大dis
    //num[i]表示i到根黑点个数
    //ma[i]表示i子树一条根到子树内某个节点,最大的黑点个数 
    //dis[i]表示i子树一条根到子树内的某个节点,路径长度 
    void dfs_num(int x, int pre, int r)
    {
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v] || v == pre) continue;
    		num[v] = num[x] + tag[v], ma[r] = max(ma[r], num[v]), dfs_num(v, x, r);
    	}
    }
    void dfs_dis(int x, int pre, int tmp)
    {
    	if(num[x] <= k) ans = max(ans, f[k - num[x]] + dis[x]);
    	if(num[x] <= k && num[x] + tmp <= k) ans = max(ans, f[tmp] + dis[x]);
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v] || v == pre) continue;
    		dis[v] = dis[x] + edge[pos].w, dfs_dis(v, x, tmp);
    	}
    }
    void dfs_f(int x, int flag, int pre)
    {
    	if(num[x] <= k)
    	{
    		if(flag) f[num[x]] = max(f[num[x]], dis[x]);
    		else f[num[x]] = 0;
    	}
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v] || v == pre) continue;
    		dfs_f(v, flag, x);
    	}
    }
    void solve(int x)
    {
    	root = 0, dfs_dp(x, -1), x = root, vis[x] = 1, tot = 0;
    	if(tag[x]) -- k;
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v]) continue;
    		num[v] = ma[v] = tag[v], dfs_num(v, -1, v), a[++ tot] = v, w[tot] = edge[pos].w;
    	}
    	for(int i = 1;i <= tot;++ i) hao[i] = i;
    	std::sort(hao + 1, hao + 1 + tot, cmp);
    	for(int i = 1;i <= tot;++ i)
    	{
    		dis[a[hao[i]]] = w[hao[i]], dfs_dis(a[hao[i]], -1, ma[a[hao[i - 1]]]), dfs_f(a[hao[i]], 1, -1);
    		int tmp = 0, r = ma[a[hao[i]]];
    		for(int j = 1;j <= r;++ j)
    			f[j] = max(f[j], tmp), tmp = max(tmp, f[j]);
    	}
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v]) continue;
    		dfs_f(v, 0, -1);
    	}
    	if(tag[x]) ++ k;
    	for(int pos = head[x];pos;pos = edge[pos].nxt)
    	{
    		int v = edge[pos].v;
    		if(vis[v]) continue;
    		sum = size[v], solve(v);
    	}
    }
    int main()
    {
    	read(n), read(k), read(m);
    	for(int i = 1;i <= m;++ i) read(tmp), tag[tmp] = 1;
    	for(int i = 1;i < n;++ i)
    	{
    		int tmp1,tmp2,tmp3;read(tmp1), read(tmp2), read(tmp3);
    		insert(tmp1, tmp2, tmp3), insert(tmp2, tmp1, tmp3);
    	}
    	ans = 0, sum = n, dp[0] = INF, solve(1);
    	printf("%d", ans);
     	return 0;
    }
    
  • 相关阅读:
    Atitit 安全措施流程法 目录 1. 常见等安全措施方法 2 1.1. 安全的语言 代码法,编译型 java 2 1.2. 安全编码法 2 1.3. 安全等框架类库 api 2 1.4. 加密法
    Atitit api与安全措施法 目录 1.1. 模板替换 sprintf %f %d 数字小数字段格式化转换校验法 1 2.  $pdo->exec 与query 2 2.1. 数字校
    Atitit 安全审计法 目录 1. 安全审计数据结构 1 2. Expame 提现流程 1 2.1. 获取提现钱的数据余额 1 2.2. 扣去余额 1 2.3. 开始safe log 2 2.4.
    Atitit 防注入 sql参数编码法 目录 1.2. 提升可读性pg_escape_literal — 转义文字以插入文本字段 1 1.2.1. 说明 1 1.3. 推荐pg_escape_str
    Atitit aes 加密法php实现
    Atitit 登录票据安全法 目录 1.1. cookie对象规范 1 1.2. Cookie加解密 1 1.3. Cookie密文动态更换,根据一个时间3天比如 1 1.4. 服务端撤销key 1
    atitit 2010 2010 diary log events memorabilia v4 tbb No finish , wait to finish ***Mon8-11 cant
    Atitit 安全流程法 目录 1. 常见等安全措施方法 2 1.1. 安全的语言 代码法,编译型 java 2 1.2. 安全编码法 2 1.3. 安全等框架类库 api 2 1.4. 加密法 2
    Atitit 数据查询法 目录 1. 数据查询语言QL (推荐) 1 1.1. Sql 1 1.2. 对象查询语言(OQL) 1 1.3. Atitit QL查询语言总结Jpql Ongl
    Atitit json数据查询法 jsonpath 目录 1.1. 1.概述 1 1.2. 3.2。经营者特殊符号 1 1.3. # JSONPath expressions 2 1.4. Xpa
  • 原文地址:https://www.cnblogs.com/huibixiaoxing/p/8804518.html
Copyright © 2011-2022 走看看