1017 - 樹分治 - Tree(POJ 1741)
阿新 • • 發佈:2018-11-10
分析
分治是個好東西,樹分治也很妙,其擅長解決樹上路徑一類的問題
在這裡主要是用的點分治(據說比邊分治簡單)
其實其思想不過也就是將一個大問題,不停的分割分割成子問題,然後需要注意(思考)的就是兩個子問題之間產生答案
挪到樹上
我們就分為兩種情況
- 處理過重心的路徑
- 處理子樹中的路徑
事實證明,TLE從來都不是卡常的鍋,你見過哪道題是需要卡常才能A的???
一般TLE都是寫掛了……比如在下
程式碼
此題不開long long也是可以的
#include<cstdio> #include<cmath> #include<algorithm> #define N 10009 #define in read() #define ll long long using namespace std; inline int read(){ char ch;int f=1,res=0; while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1; while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return f==1?res:-res; } int n,lim; int nxt[N*2],to[N*2],head[N],w[N*2],ecnt=0; void add(int x,int y,int z){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;} int sze[N],fa[N],d[N],dis[N],son[N],G,minn,num=0; bool vis[N]; ll ans=0; void dfssize(int u,int fu){//得到子樹大小,及子樹中sze最大的那一個 sze[u]=1;son[u]=0; for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(vis[v]||(v==fu)) continue; dfssize(v,u); sze[u]+=sze[v]; if(sze[v]>son[u]) son[u]=sze[v]; } } void dfsG(int rt,int u,int fu){//尋找當前圖的重心 if(sze[rt]-sze[u]>son[u]) son[u]=sze[rt]-sze[u]; if(son[u]<minn) minn=son[u],G=u; for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(vis[v]||(v==fu)) continue; dfsG(rt,v,u); } } void dfsdis(int u,int fu){//尋找每個節點到G的距離 d[num++]=dis[u]; for(int e=head[u];e;e=nxt[e]){ int v=to[e]; if(vis[v]||(v==fu)) continue; dis[v]=dis[u]+w[e]; dfsdis(v,u); } } ll calc(int rt,int L){ ll res=0;num=0; dis[rt]=L; dfsdis(rt,0); sort(d,d+num); int l=0,r=num-1; while(l<r){ if(d[l]+d[r]<=lim) { res+=r-l;//////////不能算自己 l++; } else r--; } return res; } void solve(int u){ minn=n; dfssize(u,0); dfsG(u,u,0);//以 u 為根的子樹,尋找當前的重心 vis[G]=1; ans+=calc(G,0); for(int i=head[G];i;i=nxt[i]){ if(!vis[to[i]]) { ans-=calc(to[i],w[i]); solve(to[i]); } } } int main(){ while(1){ n=in;lim=in; if(!n&&!lim) break; int i,j,k; num=ecnt=0; for(i=1;i<=n;++i) vis[i]=0,head[i]=0; for(i=1;i<n;++i){ int u=in,v=in,z=in; add(u,v,z);add(v,u,z); } ans=0; solve(1); printf("%lld\n",ans); } return 0; }