BZOJ 2599: [IOI2011]Race (点分治)
题目描述
给一棵树,每条边有权。求一条简单路径,权值和等于$K$ ,且边的数量最小。
输入输出格式
输入格式:
第一行:两个整数$n,k$。
第二至$n$行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从$n$开始)。
输出格式:
一个整数,表示最小边数量。
如果不存在这样的路径,输出 -1−1 。
输入输出样例
说明
$n\le 200000,K\le 1000000$
题解
我们用一个数组$f[j]$表示点分到当前子树中时路径长度为$j$的最小边数
然后每一棵子树里去$dfs$一遍更新$f$数组,然后用$f$数组相加更新答案
复杂度为$O(nlogn)$
1 //minamoto 2 #include<cstdio> 3 #include<iostream> 4 #define ll long long 5 using namespace std; 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 template<class T>inline int cmax(T&a,const T&b){return a<b?a=b,1:0;} 9 template<class T>inline int cmin(T&a,const T&b){return a>b?a=b,1:0;} 10 inline int read(){ 11 #define num ch-'0' 12 char ch;bool flag=0;int res; 13 while(!isdigit(ch=getc())) 14 (ch=='-')&&(flag=true); 15 for(res=num;isdigit(ch=getc());res=res*10+num); 16 (flag)&&(res=-res); 17 #undef num 18 return res; 19 } 20 char sr[1<<21],z[20];int C=-1,Z; 21 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 22 inline void print(int x){ 23 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 24 while(z[++Z]=x%10+48,x/=10); 25 while(sr[++C]=z[Z],--Z);sr[++C]='\n'; 26 } 27 const int N=200005,K=1000005; 28 int ver[N<<1],head[N],Next[N<<1],edge[N<<1]; 29 int n,k,ans,tot,size,sz[N],son[N],rt,cnt[K];bool vis[N];ll d[N]; 30 inline void add(int u,int v,int e){ 31 ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e; 32 ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e; 33 } 34 void findrt(int u,int fa){ 35 sz[u]=1,son[u]=0; 36 for(int i=head[u];i;i=Next[i]){ 37 int v=ver[i];if(v==fa||vis[v]) continue; 38 findrt(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]); 39 } 40 cmax(son[u],size-sz[u]); 41 if(son[u]<son[rt]) rt=u; 42 } 43 void dfs(int u,int fa,int dep){ 44 if(d[u]>=0&&d[u]<=k) cmin(ans,dep+cnt[k-d[u]]); 45 for(int i=head[u];i;i=Next[i]){ 46 int v=ver[i]; 47 if(v!=fa&&!vis[v]){ 48 d[v]=d[u]+edge[i],dfs(v,u,dep+1); 49 } 50 } 51 } 52 void update(int u,int fa,int dep,int opt){ 53 if(d[u]>=0&&d[u]<=k) 54 opt?cmin(cnt[d[u]],dep):cnt[d[u]]=n; 55 for(int i=head[u];i;i=Next[i]){ 56 int v=ver[i]; 57 if(!vis[v]&&v!=fa) 58 update(v,u,dep+1,opt); 59 } 60 } 61 void solve(int u){ 62 vis[u]=true,cnt[0]=0; 63 for(int i=head[u];i;i=Next[i]){ 64 int v=ver[i]; 65 if(!vis[v]) 66 d[v]=edge[i],dfs(v,0,1),update(v,0,1,1); 67 } 68 for(int i=head[u];i;i=Next[i]){ 69 int v=ver[i]; 70 if(!vis[v]) 71 update(v,0,1,0); 72 } 73 int totsz=size; 74 for(int i=head[u];i;i=Next[i]){ 75 int v=ver[i]; 76 if(!vis[v]){ 77 rt=0,size=sz[v]; 78 findrt(v,0),solve(rt); 79 } 80 } 81 } 82 int main(){ 83 n=read(),k=read(),ans=n; 84 for(int i=1;i<n;++i){ 85 int u=read()+1,v=read()+1,e=read();add(u,v,e); 86 } 87 for(int i=1;i<=k;++i) cnt[i]=n; 88 son[rt=0]=n+1,size=n,findrt(1,0),solve(rt); 89 print(ans==n?-1:ans); 90 Ot(); 91 return 0; 92 }
深深地明白自己的弱小