bzoj 2599(点分治)
2599: [IOI2011]Race
Time Limit: 70 Sec Memory Limit: 128 MBSubmit: 3642 Solved: 1081
[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
/* 开一个100W的数组t,t[i]表示权值为i的路径最少边数 找到重心分成若干子树后, 得出一棵子树的所有点到根的权值和x,到根a条边,用t[k-x]+a更新答案,全部查询完后 然后再用所有a更新t[x] 这样可以保证不出现点分治中的不合法情况 把一棵树的所有子树搞完后再遍历所有子树恢复T数组,如果用memset应该会比较慢 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define inf 1000000000 #define maxn 1000005 #define maxx 200005 using namespace std; int n,K,cnt,sum,root,ans,x,y,z; int tot[maxn],head[maxx],son[maxx],f[maxx],dis[maxx],d[maxx]; bool vis[maxx]; struct edge { int to,next,w; }e[maxx<<1]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void add(int u,int v,int w) { e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w; } void get_root(int now,int fa) { son[now]=1;f[now]=0; for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(v!=fa&&!vis[v]) { get_root(v,now); son[now]+=son[v];f[now]=max(f[now],son[v]); } } f[now]=max(f[now],sum-son[now]); if(f[now]<f[root]) root=now; } void cal(int now,int fa) { if(dis[now]<=K) ans=min(ans,d[now]+tot[K-dis[now]]); for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(!vis[v]&&v!=fa) { d[v]=d[now]+1; dis[v]=dis[now]+e[i].w; cal(v,now); } } } void updata(int now,int fa,int flag) { if(dis[now]<=K) { if(flag) tot[dis[now]]=min(tot[dis[now]],d[now]); else tot[dis[now]]=inf; } for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(!vis[v]&&v!=fa) updata(v,now,flag); } } void work(int now) { vis[now]=1;tot[0]=0; for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(!vis[v]) { d[v]=1;dis[v]=e[i].w; cal(v,0);updata(v,0,1); } } for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(!vis[v]) updata(v,0,0);//去掉重心之后要重新统计 } for(int i=head[now];i;i=e[i].next) { int v=e[i].to; if(!vis[v]) { root=0;sum=son[v]; get_root(v,0); work(root); } } } int main() { n=read();K=read(); for(int i=1;i<=K;i++)tot[i]=n; for(int i=1;i<n;i++) { x=read();y=read();z=read(); x++;y++; add(x,y,z);add(y,x,z); } ans=sum=f[0]=n;get_root(1,0); work(root); if(ans!=n)printf("%d\n",ans); else puts("-1"); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。