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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)

發布時間:2024/4/11 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 牛客多校1 - Infinite Tree(虚树+换根dp+树状数组) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出一個無窮個節點的樹,對于每個大于 1 的點 i 來說,可以向點 i / minvid[ i ] 連邊,這里的 mindiv[ x ] 表示的是 x 的最小質因數,現在給定 m 個點分別是 1! , 2! , 3! ... ,每個點都有個權值 w[ i ] ,現在需要找出一個點 u ,使得最小,輸出這個最小值

題目分析:先說收獲,通過這個題讓我稍微明白了一點換根dp。。話說為什么不直接去刷換根dp的題目,感覺有點多此億舉

再說簡單的部分,如果 m 個節點的樹已經求出來了,猜也能猜出來點 u 一定是 m 個點中的一個點,所以我們可以通過樹形dp的換根維護一下最小值就能得到答案了,具體操作如下:

設 dp1[ i ] 為以點 i 為子樹時的點權之和,轉移方程為:dp1[ u ] += dp1[ v ],換根時的轉移方程為:dp1[ u?] -= dp1[ v?] , dp1[ v ] += dp1[ u ]

然后設 dp2[ i ] 為以點 i 為子樹是題目中公式的結果,因為子樹的權值和已經通過 dp1 轉移好了,現在只需要結合距離,也就是深度差就可以轉移 dp2 了,可以想象為?u 的所有子節點 v 所代表的子樹,需要沿著 u - v 這條路徑向上轉移,也就是 dp2[ u ] += dp2[ v ] + ( deep[ v ] - deep[ u ] ) * dp1[ v ] ,當然換根時的轉移方程也是大同小異:dp2[ u ] -= dp2[ v ] + ( deep[ v ] - deep[ u ] ) * dp1[ v ] ,dp2[ v ] += dp2[ u ] +?( deep[ v ] - deep[ u ] ) * dp1[ u?]

注意一下換根時的先后順序,應該先在當前節點 u 中減去子節點 v 的貢獻,然后再在 v 中加上 u 的貢獻

轉移完成后,dp2[ root?] 就是當 u 選為 root 時的答案,為了方便起見,將 root 設為 1 即可

然后就是比較難的虛樹部分了,因為涉及到了階乘,所以原圖中節點的數量會非常龐大,但經過上面的分析,大量的節點都是無用點,只有 1!,2!,3! ... i! ... m! 最多 m 個節點是有用的節點,這樣不難想到虛樹,但因為無法建立原圖,所以不能按照常規的方式構造虛樹,這里需要更加深入了解一下虛樹的構造方法:https://www.cnblogs.com/zwfymqz/p/9175152.html

讀完上面的博客后,我們已經知道,如果想要構造出虛樹,必須要知道的是:

  • m 個節點的 dfn ( dfs序 )
  • m 個節點排好序后,所有相鄰節點的 lca 的 dfn
  • 所以此時的關鍵信息就是需要知道,m 個節點的 dfn,下面借用一下zx學長的圖片:

    如圖就是含有 1! , 2! , 3! , 4! , 5! , 6! 的一棵樹,省略了很多無用節點

    可以通過觀察出的一些結論就是:

  • 對于每一個 i 來說,點 1 到點 i! 的鏈的長度是與 i 呈正相關的關系
  • 還是上面提到的鏈,質因子的大小不增,例如 1 ~ 5! 這條鏈的邊權依次為:5 3 2 2 2
  • 將結論 1 具化一下就是 d[ i + 1 ] = d[ i ] + ( i + 1 的質因子個數 )
  • 對于 1 ~ i! 這條鏈來說,一定是由 1 ~ ( i - 1?)! 這條鏈加上 i 的質因子繼承過來的,例如 1 ~ 3! 的邊權依次為:3 2,而 1 ~ 4! 的邊權依次為 3 2 2 2,因為 4 的質因子為 2 * 2 ,所以 1 ~ 4! 的邊權在 1 ~ 3! 的基礎上增加了兩個 2
  • 按照上圖構造的樹,點 i! 和點 i 的 dfn 是呈正相關的關系
  • 假如唯一分解?的形式,那么 dis( 1 , i! ) = e1 + e2 + e3 + ... + ek
  • 現在問題就剩下了,如何求兩個點相鄰節點 ( ( i - 1 )! , i! ) 的 lca 的 dfn

    上面的第四個結論提到了,1 ~ i! 這條鏈是從?1 ~ ( i - 1 )! 這條鏈繼承而來的,再結合第二個結論,得出?lca( ( i - 1 )! , i! ) 一定是由( i?- 1 )! 和 i! 前面的最大公共質因子組成的一條鏈,而 1 ~ i! 這條鏈相對于 1 ~ ( i - 1 )! 這條鏈多出了 i 的質因子條邊,所以只需要在 1 ~ ( i - 1 )! 這條鏈上找到 i 的最大質因子的位置即可,這樣就能根據第六個結論計算出 dfn[ lca ] 了,可以用線段樹或樹狀數組來實現這個功能,只需要在迭代的同時,利用數據結構維護一下當前 i! 這條鏈上每個質因子的出現次數即可

    注意在這個題目中, dfn 和 deep 我都用了 dfn 數組表示,然后 dfn[ i?] 表示的是點 i 的 dfn,而 dfn[ i + n ] 代表的是 lca( ( i - 1 )! , i! ) 的 dfn

    代碼:
    ?

    #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;int n,w[N],mindiv[N],c[N],dfn[N],st[N],top;LL dp1[N],dp2[N],ans;vector<int>node[N];int lowbit(int x) {return x&(-x); }void add(int pos) {while(pos<=n){c[pos]++;pos+=lowbit(pos);} }int ask(int pos) {int ans=0;while(pos){ans+=c[pos];pos-=lowbit(pos);}return ans; }void build() {dfn[1]=1;for(int i=2;i<=n;i++){dfn[i]=dfn[i-1];int j=i;while(j!=mindiv[j])//求出i的最大質因子 j/=mindiv[j];dfn[i+n]=ask(n)-ask(j-1)+1;//dfn[i+n]=dfn[lca((i-1)!,i!)]j=i;while(j!=1)//更新樹狀數組維護的鏈 {dfn[i]++;add(mindiv[j]);j/=mindiv[j];}}int top=0;st[top]=1;for(int i=2;i<=n;i++){int x=i,y=i+n;while(top&&dfn[st[top-1]]>=dfn[y]){node[st[top-1]].push_back(st[top]);top--;}if(dfn[y]!=dfn[st[top]]){node[y].push_back(st[top]);st[top]=y;}st[++top]=x;}while(top){node[st[top-1]].push_back(st[top]);top--;} }void dfs1(int u) {dp1[u]=w[u];for(auto v:node[u]){dfs1(v);dp1[u]+=dp1[v];dp2[u]+=dp2[v]+dp1[v]*(dfn[v]-dfn[u]);} }void dfs2(int u) {ans=min(ans,dp2[u]);for(auto v:node[u]){LL u1=dp1[u],u2=dp2[u],v1=dp1[v],v2=dp2[v];dp2[u]-=dp2[v]+dp1[v]*(dfn[v]-dfn[u]);dp1[u]-=dp1[v];dp1[v]+=dp1[u];dp2[v]+=dp2[u]+dp1[u]*(dfn[v]-dfn[u]);dfs2(v);dp1[u]=u1,dp2[u]=u2,dp1[v]=v1,dp2[v]=v2;} }void init(int n) {ans=1e18;for(int i=0;i<=n<<1;i++){w[i]=c[i]=dp1[i]=dp2[i]=0;node[i].clear();} }void init()//預處理mindiv[x]:x的最小質因子 {mindiv[1]=1;for(int i=2;i<N;i++)if(!mindiv[i])for(int j=i;j<N;j+=i)if(!mindiv[j])mindiv[j]=i; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);init();while(scanf("%d",&n)!=EOF){init((n<<1)+5);for(int i=1;i<=n;i++)scanf("%d",w+i);build();//建虛樹dfs1(1);dfs2(1);printf("%lld\n",ans);}return 0; }

    ?

    總結

    以上是生活随笔為你收集整理的牛客多校1 - Infinite Tree(虚树+换根dp+树状数组)的全部內容,希望文章能夠幫你解決所遇到的問題。

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