1. 程式人生 > >【洛谷4149】[IOI2011] Race(點分治)

【洛谷4149】[IOI2011] Race(點分治)

點此看題面

大致題意: 給你一棵樹,問長度為KK的路徑至少由幾條邊構成。

點分治

這題應該比較顯然是點分治

LinkLink

點分治 詳見部落格 初學點分治

主要思路

與常見的點分治套路一樣,由於K1000000K≤1000000,因此我們可以考慮開個桶ff陣列來記錄每種長度的路徑至少由幾條邊構成

但是要注意,每換一個根要將桶清空

呃,暴力清空肯定TT飛。

於是就需要再開一個gg陣列,記錄每個答案是在以哪一個節點為根時求出來的,這樣就可以避免清空陣列了。這也是一個比較常用的套路。

其餘的過程與點分板子差不多,就不多說了。

程式碼

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)<(y)?(x):(y)) #define uint unsigned int #define LL long long #define ull unsigned long long #define swap(x,y) (x^=y,y^=x,x^=y) #define abs(x) ((x)<0?-(x):(x)) #define INF 1e9 #define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD)) #define
ten(x) (((x)<<3)+((x)<<1))
#define N 200000 #define K 1000000 #define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=z) using namespace std; int n,k,ee=0,lnk[N+5]; struct edge { int to,nxt,val; }e[2*N+5]; class FIO { private: #define Fsize 100000 #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch)) int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize]; public: FIO() {FinNow=FinEnd=Fin;} inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;} inline void read_char(char &x) {while(isspace(x=tc()));} inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;} inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;} inline void write_char(char x) {pc(x);} inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);} inline void end() {fwrite(Fout,1,FoutSize,stdout);} }F; class Class_DotSolver//點分治 { private: int ans,rt,top,f[K+5],g[K+5],used[N+5],Size[N+5],Max[N+5],dis[N+5],len[N+5],Stack[N+5]; inline void GetRt(int x,int lst,int sum)//找重心做根 { register int i; for(i=lnk[x],Size[x]=1,Max[x]=0;i;i=e[i].nxt) if(e[i].to^lst&&!used[e[i].to]) GetRt(e[i].to,x,sum),Size[x]+=Size[e[i].to],Max[x]=max(Max[x],Size[e[i].to]); if((Max[x]=max(Max[x],sum-Size[x]))<Max[rt]) rt=x; } inline void dfs(int x,int lst)//遍歷子樹,將各種路徑儲存下來 { register int i; for(i=lnk[Stack[++top]=x];i;i=e[i].nxt) if(e[i].to^lst&&!used[e[i].to]) dis[e[i].to]=dis[x]+e[i].val,len[e[i].to]=len[x]+1,dfs(e[i].to,x); } inline void Solve(int x)//在以x為根的子樹中求解 { register int i,j; for(i=lnk[x],used[x]=1,g[0]=x;i;i=e[i].nxt) { if(used[e[i].to]) continue; dis[e[i].to]=e[i].val,len[e[i].to]=1,dfs(e[i].to,x);//遍歷該子樹 for(j=top;j;--j) if(dis[Stack[j]]<=k&&!(g[k-dis[Stack[j]]]^x)) ans=min(ans,len[Stack[j]]+f[k-dis[Stack[j]]]);//更新答案 while(top) { if(dis[Stack[top]]<=k) g[dis[Stack[top]]]^x?(g[dis[Stack[top]]]=x,f[dis[Stack[top]]]=len[Stack[top]]):(f[dis[Stack[top]]]=min(f[dis[Stack[top]]],len[Stack[top]]));//更新桶 --top; } } for(i=lnk[x];i;i=e[i].nxt) if(!used[e[i].to]) GetRt(e[i].to,rt=0,Size[e[i].to]),Solve(rt);//繼續對子樹進行處理 } public: inline int GetAns() {return (void)(ans=Max[0]=INF,GetRt(1,rt=0,n),Solve(rt)),ans==INF?-1:ans;}//求答案 }DotSolver; int main() { register int i,x,y,z; for(F.read(n),F.read(k),i=1;i<n;++i) F.read(x),F.read(y),F.read(z),++x,++y,add(x,y,z),add(y,x,z);//注意細節,是從0開始編號的,所以將x和y各加1 return F.write(DotSolver.GetAns()),F.end(),0; }