BZOJ2599: [IOI2011]Race

n<=200000的边权树上问长度=K<=1000000的链中边数最少的。

点分治。开个桶统计各个长度的答案。

 1 #include<string.h>
 2 #include<stdlib.h>
 3 #include<stdio.h>
 4 #include<math.h>
 5 #include<algorithm>
 6 //#include<bitset>
 7 //#include<queue>
 8 //#include<iostream>
 9 using namespace std;
10 
11 int n,K;
12 #define maxn 200011
13 #define maxk 1000011
14 struct Edge{int to,v,next;}edge[maxn<<1]; int first[maxn],le=2;
15 void in(int x,int y,int v) {Edge &e=edge[le]; e.to=y; e.v=v; e.next=first[x]; first[x]=le++;}
16 void insert(int x,int y,int v) {in(x,y,v); in(y,x,v);}
17 
18 int size[maxn]; bool die[maxn];
19 void getsize(int x,int fa)
20 {
21     size[x]=1;
22     for (int i=first[x];i;i=edge[i].next)
23     {
24         const Edge &e=edge[i]; if (e.to==fa || die[e.to]) continue;
25         getsize(e.to,x); size[x]+=size[e.to];
26     }
27 }
28 
29 int getroot(int x,int fa,int tot)
30 {
31     for (int i=first[x];i;i=edge[i].next)
32     {
33         const Edge &e=edge[i]; if (e.to==fa || die[e.to]) continue;
34         if (size[e.to]*2>tot) return getroot(e.to,x,tot);
35     }
36     return x;
37 }
38 
39 int f[maxk];
40 void calc(int x,int fa,int now,int cur)
41 {
42     if (now>K) return;
43     f[K]=min(f[K],f[K-now]+cur);
44     for (int i=first[x];i;i=edge[i].next)
45     {
46         const Edge &e=edge[i]; if (e.to==fa || die[e.to]) continue;
47         calc(e.to,x,now+e.v,cur+1);
48     }
49 }
50 
51 void gao(int x,int fa,int now,int v,bool op)
52 {
53     if (now>K) return;
54     if (op) f[now]=min(f[now],v);
55     else (now!=K && (f[now]=0x3f3f3f3f));
56     for (int i=first[x];i;i=edge[i].next)
57     {
58         const Edge &e=edge[i]; if (e.to==fa || die[e.to]) continue;
59         gao(e.to,x,now+e.v,v+1,op);
60     }
61 }
62 
63 void cd(int x)
64 {
65     die[x]=1; f[0]=0;
66     for (int i=first[x];i;i=edge[i].next)
67     {
68         const Edge &e=edge[i]; if (die[e.to]) continue;
69         calc(e.to,x,e.v,1); gao(e.to,x,e.v,1,1);
70     }
71     for (int i=first[x];i;i=edge[i].next)
72     {
73         const Edge &e=edge[i]; if (die[e.to]) continue;
74         gao(e.to,x,e.v,0,0);
75     }
76     for (int i=first[x];i;i=edge[i].next)
77     {
78         const Edge &e=edge[i]; if (die[e.to]) continue;
79         getsize(e.to,x); cd(getroot(e.to,x,size[e.to]));
80     }
81 }
82 
83 int main()
84 {
85     scanf("%d%d",&n,&K);
86     for (int i=1,x,y,v;i<n;i++)
87     {
88         scanf("%d%d%d",&x,&y,&v); x++; y++;
89         insert(x,y,v);
90     }
91     getsize(1,0);
92     for (int i=1;i<=K;i++) f[i]=0x3f3f3f3f;
93     cd(getroot(1,0,size[1]));
94     printf("%d\n",f[K]==0x3f3f3f3f?-1:f[K]);
95     return 0;
96 }
View Code

 

转载于:https://www.cnblogs.com/Blue233333/p/8350690.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值