[IOI 2011]Race

Description

给一棵树,每条边有非负权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, 1 <= K <= 1000000

Input

第一行 两个整数 n, k

第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)

Output

一个整数 表示最小边数量 如果不存在这样的路径 输出-1

Sample Input

4 3

0 1 1

1 2 2

1 3 4

Sample Output

2

题解

做了一上午的狗屎题,其实很水(点分裸题)...老是找不出细节错误....

  • 在每一棵点分治的树中只考虑经过根的路径;
    • (1)某一点到根的路径
      • 只需要算出每个点到根的距离即可判断。
    • (2)来自根节点不同儿子所在子树的两个点构成的路径
      • 每个点相当于有三个参数$belong[i]$,$dis[i]$,$s[i]$,分别表示删除根后属于哪个联通快,到根的路径长度以及路径上的边数;
      • 原问题相当于求$min(s[i]+s[j])$,$belong[i]!=belong[j]$,$dis[i]+dis[j]=k$。
  • 依次处理根的每一棵子树;
  • $f[i]$表示已经处理过的子树中到根距离为$i$的点中$s$值最小为多少;
  • 当处理下一棵子树时,每个点所能匹配的点到根的距离都是固定的,直接拿出对应的$f$值更新答案即可,然后利用这棵子树更新$f$数组;
  • 这样保证了更新答案的两点$belong$值不同,$dis$相加等于$k$,同时直接找出当前最优解。
  • 易发现,所有路径都是在这个方法中考虑过的,显然是可行的。

 

  1 //It is made by Awson on 2017.9.20
  2 #include <set>
  3 #include <map>
  4 #include <cmath>
  5 #include <ctime> 
  6 #include <queue>
  7 #include <stack>
  8 #include <string>
  9 #include <cstdio>
 10 #include <vector>
 11 #include <cstdlib>
 12 #include <cstring>
 13 #include <iostream>
 14 #include <algorithm>
 15 #define Min(a, b) ((a) < (b) ? (a) : (b))
 16 #define Max(a, b) ((a) > (b) ? (a) : (b))
 17 #define LL long long
 18 using namespace std;
 19 const int N = 200000;
 20 const int K = 1000000;
 21 const int INF = ~0u>>1;
 22 
 23 inline int Read() {
 24     int sum = 0;
 25     char ch = getchar();
 26     while (ch < '0' || ch > '9') ch = getchar();
 27     while (ch >= '0' && ch <= '9') sum = (sum<<1) + (sum<<3) + ch - 48, ch = getchar();
 28     return sum;
 29 }
 30 int n, k, u, v, c;
 31 struct tt {
 32     int to, cost, next;
 33 }edge[N*2+5];
 34 int path[N+5], top;
 35 int ans = INF;
 36 int size[N+5], mxsize[N+5];
 37 bool vis[N+5];
 38 int minsize, root;
 39 int f[K+5];
 40 
 41 inline void add(int u, int v, int c) {
 42     edge[++top].to = v;
 43     edge[top].cost = c;
 44     edge[top].next = path[u];
 45     path[u] = top;
 46 }
 47 void dfs_size(int u, int fa) {
 48     size[u] = 1;
 49     mxsize[u] = 0;
 50     for (int i = path[u]; i; i = edge[i].next)
 51         if ((!vis[edge[i].to]) && edge[i].to != fa) {
 52             dfs_size(edge[i].to, u);
 53             size[u] += size[edge[i].to];
 54             mxsize[u] = Max(mxsize[u], size[edge[i].to]);
 55         }
 56 }
 57 void dfs_getroot(int r, int u, int fa) {
 58     mxsize[u] = Max(mxsize[u], size[r]-size[u]);
 59     if (mxsize[u] < minsize) minsize = mxsize[u], root = u;
 60     for (int i = path[u]; i; i = edge[i].next)
 61         if ((!vis[edge[i].to]) && edge[i].to != fa)
 62             dfs_getroot(r, edge[i].to, u);
 63 }
 64 void dfs_getans(int u, int fa, int cnt, int val) {
 65     if (val > k) return;
 66     if (val == k) {
 67         ans = Min(ans, cnt);
 68         return;
 69     }
 70     int tmp = k-val;
 71     if (f[tmp]) ans = Min(f[tmp]+cnt, ans);
 72     for (int i = path[u]; i; i = edge[i].next)
 73         if ((!vis[edge[i].to]) && edge[i].to != fa)
 74             dfs_getans(edge[i].to, u, cnt+1, val+edge[i].cost);
 75 }
 76 void dfs_update(int u, int fa, int cnt, int val) {
 77     if (val >= k) return;
 78     if (!f[val]) f[val] = cnt;
 79     else f[val] = Min(f[val], cnt);
 80     for (int i = path[u]; i; i = edge[i].next)
 81         if ((!vis[edge[i].to]) && edge[i].to != fa)
 82             dfs_update(edge[i].to, u, cnt+1, val+edge[i].cost);
 83 }
 84 void dfs_delete(int u, int fa, int val) {
 85     if (val >= k) return;
 86     f[val] = 0;
 87     for (int i = path[u]; i; i = edge[i].next)
 88         if ((!vis[edge[i].to]) && edge[i].to != fa)
 89             dfs_delete(edge[i].to, u, val+edge[i].cost);
 90 }
 91 void solve(int x) {
 92     minsize = INF;
 93     dfs_size(x, 0);
 94     dfs_getroot(x, x, 0);
 95     vis[root] = true;
 96     for (int i = path[root]; i; i = edge[i].next)
 97         if (!vis[edge[i].to]) {
 98             dfs_getans(edge[i].to, root, 1, edge[i].cost);
 99             dfs_update(edge[i].to, root, 1, edge[i].cost);
100         }
101     for (int i = path[root]; i; i = edge[i].next)
102         if (!vis[edge[i].to])
103             dfs_delete(edge[i].to, root, edge[i].cost);
104     for (int i = path[root]; i; i = edge[i].next)
105         if (!vis[edge[i].to])
106             solve(edge[i].to);
107 }
108 void work() {
109     for (int i = 1; i < n; i++) {
110         u = Read(); v = Read(); c = Read();
111         u++, v++;
112         add(u, v, c);
113         add(v, u, c);
114     }
115     ans = INF;
116     solve(1);
117     printf("%d\n", ans == INF ? -1 : ans);
118 }
119 int main() {
120     int size = 128 << 20;                      //==========//
121     char *p = (char*)malloc(size) + size;      //手 动 扩 栈//
122     __asm__("movl %0, %%esp\n" :: "r"(p));     //==========//
123     n = Read(); k = Read();
124     work();return 0;
125 }

 

转载于:https://www.cnblogs.com/NaVi-Awson/p/7560921.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值