日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

点分治入门

發布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 点分治入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點分治就是樹上的分治,也就是處理把分治強行搬到樹上的毒瘤問題。

算法流程

引入:[國家集訓隊]聰聰可可 (各大OJ搜索即可)

題意:給一棵樹,隨機選兩個點(可以相同),求距離為3的倍數的概率。

顯然求長度是3的倍數的路徑數即可。

首先是分治,那我們考慮一個子問題,即一棵樹。

然而樹上沒有中點。如果直接分,給條鏈就掛了。

所以我們yy一個中點:重心

重心定義是以它為根的子樹size的最大值最小。

因為是樹,所以至少有兩個子樹。又是均分的,可以保證log

我們先找出重心,然后亂搞求過重心的路徑數。

然后遞歸就可以解決。

實現

口胡和實現難度差別最大的算法。

首先需要一個findroot來找重心。

開一個cut數組記錄被砍掉的節點,相當于移出游戲 。

用solve控制流程。進入solve后,馬上砍掉root

然后馬上用calc計算當前子問題

calc中用dfs處理子樹信息并順便求出大小,然后整合答案并更新。注意清零。

此過程中一定不要吝嗇空間,可以重新開個數組記錄修改的地方,算完后再依次還原。最好開臨時數組記錄當前子樹并和之前統計答案,算完后合并。千萬不要memset,用for也要仔細算復雜度。

當然本題很友好,只有三個變量。

回到solve,用dfs中求出的大小找子樹中的重心并遞歸

代碼

風格可能和大眾不同,僅供參考

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN 20005 #define MAXM 40005 #define INF 0x7fffffff using namespace std; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } int n; struct edge {int u,v,w; }e[MAXM]; int head[MAXN],nxt[MAXM],cnt; void addnode(int u,int v,int w) {e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt; } bool cut[MAXN]; int ans; int root,siz[MAXN],maxp[MAXN]; void findroot(int u,int f,int sum) {siz[u]=1,maxp[u]=0;for (int i=head[u];i;i=nxt[i])if (e[i].v!=f&&!cut[e[i].v]){findroot(e[i].v,u,sum);siz[u]+=siz[e[i].v];maxp[u]=max(maxp[u],siz[e[i].v]);}if (sum-siz[u]>maxp[u]) maxp[u]=sum-siz[u];if (maxp[u]<maxp[root]) root=u; } int tot[3],tt[3]; int dfs(int u,int f,int s) {int ans=1;++tt[s];for (int i=head[u];i;i=nxt[i])if (e[i].v!=f&&!cut[e[i].v])ans+=dfs(e[i].v,u,(s+e[i].w)%3);return ans; } int sum[MAXN]; void calc() {tot[0]=1,tot[1]=tot[2]=0;for (int i=head[root];i;i=nxt[i])if (!cut[e[i].v]){tt[0]=tt[1]=tt[2]=0;sum[e[i].v]=dfs(e[i].v,0,e[i].w);ans+=tot[0]*tt[0]+tot[1]*tt[2]+tot[2]*tt[1];tot[0]+=tt[0],tot[1]+=tt[1],tot[2]+=tt[2];} } void solve() {cut[root]=1;calc();for (int i=head[root];i;i=nxt[i])if (!cut[e[i].v]){maxp[root=0]=INF;findroot(e[i].v,0,sum[e[i].v]);solve();} } int gcd(const int& a,const int& b){return b? gcd(b,a%b):a;} void print(int a,int b) {int g=gcd(a,b);a/=g,b/=g;printf("%d/%d",a,b); } int main() {n=read();for (int i=1;i<n;i++){int u,v,w;u=read(),v=read(),w=read()%3;addnode(u,v,w),addnode(v,u,w);}maxp[0]=INF;findroot(1,0,n);solve();print(2*ans+n,n*n);return 0; }

[IOI2011]Race

題意:給定一棵樹和k,求長為k的路徑中最小的邊數。k≤1000000k \leq 1000000k1000000

其它一樣,主要是calc

開個1e6的數組dis記錄長為i的路徑最小邊數

用tdis記錄當前子樹,dfs時更新

用q和tq記錄dis和tdis修改的下標,算完后還原成INF

#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN 200005 #define MAXM 400005 #define MAXV 1000005 #define INF 0x3f3f3f3f using namespace std; inline int read() {int ans=0,f=1;char c=getchar();while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return f*ans; } int n,k; struct edge{int u,v,w;}e[MAXM]; int head[MAXN],nxt[MAXM],cnt; void addnode(int u,int v,int w) {e[++cnt]=(edge){u,v,w};nxt[cnt]=head[u];head[u]=cnt; } bool cut[MAXN]; int root; int siz[MAXN],maxp[MAXN]; void findroot(int u,int f,int s) {siz[u]=1,maxp[u]=0;for (int i=head[u];i;i=nxt[i])if (!cut[e[i].v]&&e[i].v!=f){findroot(e[i].v,u,s);siz[u]+=siz[e[i].v];maxp[u]=max(maxp[u],siz[e[i].v]);}if (s-siz[u]>maxp[u]) maxp[u]=s-siz[u];if (maxp[u]<maxp[root]) root=u; } int dis[MAXV],tdis[MAXV],sum[MAXN]; int q[MAXN],top,tq[MAXN],tt; int dfs(int u,int f,int s,int d) {int ans=1;if (s<=k) tdis[tq[++tt]=s]=min(tdis[s],d);for (int i=head[u];i;i=nxt[i])if (!cut[e[i].v]&&e[i].v!=f)ans+=dfs(e[i].v,u,s+e[i].w,d+1);return ans; } int ans=INF; void calc() {dis[0]=top=0;for (int i=head[root];i;i=nxt[i])if (!cut[e[i].v]){tt=0;sum[e[i].v]=dfs(e[i].v,0,e[i].w,1);for (int i=1;i<=tt;i++) ans=min(ans,tdis[tq[i]]+dis[k-tq[i]]);for (int i=1;i<=tt;i++) dis[tq[i]]=min(dis[tq[i]],tdis[tq[i]]),q[++top]=tq[i];for (int i=1;i<=tt;i++) tdis[tq[i]]=INF; }for (int i=1;i<=top;i++) dis[q[i]]=INF; } void solve() {cut[root]=true;calc();for (int i=head[root];i;i=nxt[i])if (!cut[e[i].v]){maxp[root=0]=INF;findroot(e[i].v,0,sum[e[i].v]);solve();} } int main() {n=read(),k=read();for (int i=1;i<n;i++){int u,v,w;u=read()+1,v=read()+1,w=read();addnode(u,v,w),addnode(v,u,w);}maxp[root=0]=INF;findroot(1,0,n);memset(dis,0x3f,sizeof(dis)+sizeof(tdis));solve();printf("%d\n",(ans==INF? -1:ans));return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的点分治入门的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。