2599: [IOI2011]Race
Time Limit: 70 Sec Memory Limit: 128 MBSubmit: 2796 Solved: 824
[ Submit][ Status][ Discuss]
Description
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000
Input
第一行 两个整数 n, k
第二..n行 每行三个整数 表示一条无向边的两端和权值 (注意点的编号从0开始)
Output
一个整数 表示最小边数量 如果不存在这样的路径 输出-1
Sample Input
4 3
0 1 1
1 2 2
1 3 4
0 1 1
1 2 2
1 3 4
Sample Output
2
HINT
Source
题解:点分治
建一个num数组,num[i]表示边的数量为i的路径的个数。
在统计答案的时候,我们现将当前点子树中的路径都加入答案,然后在将不合法的减去。
最后从小到大,找第一个num[i]不为0的i
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 500003
using namespace std;
int point[N],next[N],v[N],len[N],num[1000003],tot,ans,sz;
int n,m,deep[N],root,son[N],f[N],sum,dis[N],d[N],vis[N];
int cnt[N];
struct data
{
int cnt,d;
}a[N];
void add(int x,int y,int z)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; len[tot]=z;
}
void getroot(int x,int fa)
{
son[x]=1; f[x]=0;
for (int i=point[x];i;i=next[i])
if (v[i]!=fa&&!vis[v[i]]) {
getroot(v[i],x);
son[x]+=son[v[i]];
f[x]=max(f[x],son[v[i]]);
}
f[x]=max(sum-son[x],f[x]);
if(f[x]<f[root]) root=x;
}
void getdis(int x,int fa)
{
sz++;
a[sz].d=d[x]; a[sz].cnt=cnt[x];
for (int i=point[x];i;i=next[i])
{
if (vis[v[i]]||v[i]==fa) continue;
d[v[i]]=d[x]+len[i]; cnt[v[i]]=cnt[x]+1;
getdis(v[i],x);
}
}
int cmp(data a,data b)
{
return a.d<b.d||a.d==b.d&&a.cnt<b.cnt;
}
int calc(int x,int now,int deep,int v)
{
sz=0;
d[x]=now; cnt[x]=deep;
getdis(x,0);
sort(a+1,a+sz+1,cmp);
int l=1; int r=sz;
for (int i=1;i<=sz;i++) if (a[i].d==m) ans=min(ans,a[i].cnt);
while (l<r) {
while (a[l].d+a[r].d>m&&r>l) r--;
int rr=r;
if (r<=l) break;
while (a[l].d+a[r].d==m) {
num[a[l].cnt+a[r].cnt]+=v;
r--;
if (r<=l) break;
}
r=rr;
l++;
}
}
void work(int x)
{
calc(x,0,0,1);
vis[x]=1;
for (int i=point[x];i;i=next[i])
{
if (vis[v[i]]) continue;
calc(v[i],len[i],1,-1);
sum=son[v[i]]; root=0;
getroot(v[i],root);
work(root);
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
x++; y++;
add(x,y,z);
}
f[0]=1000000000; sum=n; root=0;
getroot(1,0);
work(root);
for (int i=0;i<=n;i++)
if (num[i]>0) {
printf("%d\n",i);
return 0;
}
printf("-1\n");
}