<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      DP優化——長剖優化DP

      長鏈剖分

      在長鏈剖分中重兒子的定義為:子樹深度最大的兒子。
      其余就和重剖一樣了。
      下面是核心代碼:

      void dfs(int u){
      	mxdep[u]=0;  //子樹內最大深度
      	for(int i=head[u];i;i=Next[i]){
      		int v=to[i];
      		if(v==fa[u]) continue;
      		fa[v]=u;
      		dfs(v);
      		if(mxdep[v]>=mxdep[son[u]]) son[u]=v;
      	}
      	if(son[u]) mxdep[u]=mxdep[son[u]]+1;
      }
      

      應用

      長鏈剖分的應用很多,但因為我們放在了 DP 優化合集里,所以我們只介紹他如何優化 DP。

      要用他優化 DP 我們只需要知道他的一個性質就夠了:所有長鏈的長度之和為 O(n)

      能用長鏈剖分優化的 DP 滿足其中一維 dp 狀態跟深度有關,此時如果我們對每一個點那一維都開一個 \(O(n)\) 的數組,那么空間和時間起碼就是 \(O(n^2)\) 的了。
      但其實對于每個點 \(u\) 他那一維的大小實際上只需要開 \(maxdep_u\) 的大小即可,有很多是浪費的。
      所以我們借鑒 dsu on tree 的思想,每個點直接繼承重兒子的 dp 數組,然后暴力合并輕兒子的 dp 數組。
      此時每一條長鏈只會在鏈頂向其父親轉移時會被暴力合并,所以總復雜度就是所有長鏈的長度之和 \(O(n)\)

      在實現繼承數組這一操作時,我們往往使用指針,在遍歷到鏈頂時提前為整條長鏈開好空間,然后這條長鏈上的點公用這段空間。
      根據 dp 轉移的不同可能會需要用不同的寫法。

      下面以一道例題來詳細介紹。

      例題

      幾乎所有介紹長剖優化 DP 的博客(包括 oi-wiki) 都以 CF1009F 作為例題,所以本博客這里不再以他為例題介紹。

      [POI2014] HOT-Hotels 加強版

      畫個圖容易發現滿足條件的三個點 \((x,y,z)\) 一定滿足兩者之一:

      1. 他們的 \(LCA\) 是同一個,且他們距離 \(LCA\) 的距離都為 \(d\)
      2. 他們其中兩個(不妨設為 \(x,y\))距離他們的 \(LCA\)(下面記為 \(u\)) 距離都為 \(d\),并且 \(z\)\(u\) 的距離也為 \(d\)。而且 \(LCA(x,z)=LCA(y,z)=lCA(u,z)=v\),即要保證他們之間的距離為 \(2d\)

      其實第 \(1\) 種情況是第 \(2\) 種情況的特殊情況。

      為了避免算重我們把每個三元組算在最高處,比如在第一種情況里就算在 \(LCA\) 處,在第二種情況里就算在 \(v\) 處。
      然后進行 dp:
      \(f_{i,j}\) 表示和 \(i\) 子樹內和 \(i\) 距離為 \(j\) 的點的個數。
      \(g_{i,j}\) 表示 \(i\) 子樹內的點對 \((x,y)\) 滿足 \(LCA(x,y)=lca,dist(x,lca)=dist(y,lca)=d\),且 \(dist(i,lca)=d-j\)\((x,y)\) 的個數。

      轉移考慮樹形背包,假設現在合并進來 \(u\) 的一個子樹 \(v\)
      \(f\) 的轉移是簡單的(和 CF1009F 一樣):\(f_{u,j+1} += f_{v,j}\)
      對于 \(g\) 的轉移分兩種:

      1. 點對來自 \(v\) 子樹內:\(g_{u,j-1} += g_{v,j}\)
      2. 點對分別來自前面的子樹和 \(v\) 子樹,此時他們的 \(lca\) 就是 \(u\)\(g_{u,j+1} += f_{u,j+1}\times f_{v,j}\)

      然后考慮對 \(ans\) 的貢獻:

      1. 最后加上 \(g_{u,0}\):此時 \(u\) 就是那第三個點。
      2. 還是考慮在合并進 \(v\) 子樹的時候,此時的貢獻分為 \(v\) 子樹提供單點和 \(v\) 子樹提供點對:\(g_{u,j+1} \times f_{v,j} + f_{u,j-1} \times g_{v,j}\)

      但其實當 \(j=1\) 時,\(f_{u,j-1}\times g_{v,j}\) 就是第 \(1\) 種情況的貢獻。
      所以不需要額外把第 \(1\) 種情況算進去。

      第二維跟子樹最大深度同階,可以長剖優化成 \(O(n)\)
      要注意的是 \(g\) 數組在繼承重兒子的時候是 \(g_{u,j-1}=g_{son_u,j}\),而不是像 \(f\) 那樣 \(f_{u,j+1}=f_{son_u,j}\),所以內存分配的方式有所不同。

      code

      #include<bits/stdc++.h>
      #define int long long  
      using namespace std;
      const int N=1e5+5;
      
      inline int read(){
      	int w=1,s=0;
      	char c=getchar();
      	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
      	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
      	return w*s;
      }
      int n,tot,head[N],to[N<<1],Next[N<<1];
      void add(int u,int v){
      	to[++tot]=v,Next[tot]=head[u],head[u]=tot;
      }
      int mxdep[N],son[N],fa[N],*f[N],*g[N],ans,tmp1[N<<2],tmp2[N<<2],*id1=tmp1,*id2=tmp2;  //f,g 只是指向內存開頭的指針,tmp1,tmp2 是用來分配內存的數組
      void dfs(int u){
      	mxdep[u]=0;
      	for(int i=head[u];i;i=Next[i]){
      		int v=to[i];
      		if(v==fa[u]) continue;
      		fa[v]=u;
      		dfs(v);
      		if(mxdep[v]>=mxdep[son[u]]) son[u]=v;
      	}
      	if(son[u]) mxdep[u]=mxdep[son[u]]+1;
      }
      void dfs1(int u){
      	if(son[u]){
      		f[son[u]]=f[u]+1;  // 此時對于 son[u] 來講的 dp 數組的第 i 位,就是對于 u 來講的 dp 數組的第 i+1 位,下同
      		g[son[u]]=g[u]-1;
      		dfs1(son[u]);
      		ans += g[son[u]][1];
      	}
      	f[u][0]=1;
      	for(int i=head[u];i;i=Next[i]){
      		int v=to[i];
      		if(v==fa[u] || v==son[u]) continue;
      		f[v]=id1; id1+=mxdep[v]+1;  //在鏈頂分配內存,對于 f 只需要往后分配 maxdep[v] 的長度即可
      		id2+=mxdep[v]; g[v]=id2; id2+=mxdep[v]+1;   //對于 g 不僅需要往后分配還需要往前分配(因為重兒子 dp 數組的開頭在他的開頭前面)
      		dfs1(v);
      		for(int j=0;j<=mxdep[v];j++) ans += g[u][j+1] * f[v][j] + ((j>0) ? (f[u][j-1] * g[v][j]) : 0); 
      		for(int j=0;j<=mxdep[v];j++){
      			g[u][j+1]+=f[u][j+1]*f[v][j];
      			if(j>0) g[u][j-1]+=g[v][j];
      		}
      		for(int j=0;j<=mxdep[v];j++) f[u][j+1]+=f[v][j];
      	}
      }
      signed main(){
      	n=read();	
      	for(int i=1;i<n;i++){
      		int u=read(),v=read();
      		add(u,v),add(v,u); 
      	}
      	
      	dfs(1);
      	f[1]=id1; id1+=mxdep[1]+1;
      	id2+=mxdep[1]; g[1]=id2; id2+=mxdep[1]+1; 
      	dfs1(1);
      	
      	printf("%lld\n",ans);
      	return 0;
      }
      
      posted @ 2025-01-03 14:04  Green&White  閱讀(125)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲中文字幕一区精品自| 亚洲性线免费观看视频成熟| 在厨房拨开内裤进入在线视频| 亚洲男人的天堂一区二区| 欧美18videosex性欧美tube1080| 国产成人精选视频在线观看不卡| 庆云县| 国产成人亚洲无码淙合青草| 久久精品国产99国产精品| 国产精品男女午夜福利片| 沭阳县| 中文字幕日韩有码av| 熟妇无码熟妇毛片| 欧美日韩一区二区综合| 伊人久久大香线蕉综合网站| 亚洲欧洲中文日韩久久av乱码| 国产精品日韩中文字幕| 亚洲国产成人一区二区在线| 国产激情国产精品久久源| 欧美怡春院一区二区三区| 亚洲成a人无码av波多野| 国产福利萌白酱在线观看视频 | 在线播放亚洲人成电影| 视频一区视频二区卡通动漫| 国产乱妇乱子视频在播放| 国产喷水1区2区3区咪咪爱AV| 国产成人欧美一区二区三区| 国产精品99区一区二区三| 婷婷成人丁香五月综合激情| 色综合中文字幕色综合激情| 亚洲熟妇精品一区二区| 91麻豆亚洲国产成人久久| 国产欧美日韩亚洲一区二区三区| 精品一区二区亚洲国产| 亚洲avav天堂av在线网爱情| 最新偷拍一区二区三区| 18av千部影片| 成人自拍短视频午夜福利| 丝袜美腿亚洲综合在线观看视频| 精品粉嫩国产一区二区三区| 苍井空毛片精品久久久|