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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

并不对劲的长链剖分

發布時間:2024/4/17 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 并不对劲的长链剖分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

真正強的人

真正強的人被稱為管老師或很對勁的太刀流(點這里)

長鏈剖分是什么?

重鏈剖分是對于每個點,找出它的兒子中子樹最大的,稱為重(zhong四聲)兒子,在它和重兒子之間連重邊,其他邊是輕邊,由重邊組成的連是重鏈
這樣就把一棵樹拆成了很多條重鏈,可以將樹上的問題變成鏈上的問題,時間復雜度會和每個點到根的路徑上的輕邊數(約log n條)有關
類似的,長鏈剖分是對于每個點,找出它的兒子中到葉子節點最遠的,稱為長(chang二聲)兒子,在它和長兒子之間連長邊,其他邊是短邊,由長邊組成的連是長鏈
這樣就把一棵樹拆成了很多條長鏈,它有很多有趣的性質

用途

  • 求點\(u\)\(k\)級祖先(可跳過)

    講解

    一般是用倍增,預處理的時間復雜度是\(\Theta(n\space log \space n)\),單次詢問\(\Theta(log \space n)\)
    結合長鏈剖分能做到\(\Theta(n\space log \space n)-\Theta(1)\)
    預處理時除了倍增數組以外,還要對于每條長鏈的鏈頂,設鏈長為\(x\),則要記它往上走1到\(x\)個點分別是哪些,往下走1到\(x\)個點分別是哪些,這一步需要\(\Theta(n)\)的時間和空間,因為\(\Sigma{鏈長}=n\)
    查詢時,先從點\(u\)向上走\(2^{highbit(k)}\)(highbit(x)和lowbit的定義類似,是指x二進制中最高位的位置)步,記跳到的點為\(v\)
    因為\(dis(u,v)=2^{highbit(k)}\),所以\(v\)所在長鏈的長度肯定大于\(2^{highbit(k)}\),而從\(v\)走到\(u\)\(k\)級祖先,還需要不到\(2^{highbit(k)}\)步,那么\(u\)\(k\)級祖先一定會出現在\(v\)的鏈頂記錄的區域中
    這個過程是預處理->從\(u\)向上\(2^{highbit(k)}\)步->看當前點鏈頂預處理出的區域

    例題
    沒有找到例題,可能是因為只有在查詢次數較多時這種方法才有優勢,而且長鏈剖分常數比較大,優勢不明顯
  • 優化dp

    講解

    當dp狀態有一維是點,有一維是深度,而且每個點的dp值都是從它的兒子合并來的時,就可以用長鏈剖分優化了
    每條長鏈會共用一個數組,在鏈頂處會將長度為鏈長的數組合并到另一條鏈上
    因為\(\Sigma{鏈長}=n\),所以總空間復雜度是\(\Theta(n)\),時間復雜度會和合并的時間復雜度有關

    例題

    有一棵\(n\)(\(n\leq2*10^6\))個點的樹,每個點\(i\)有點權\(w_i\)(\(w_i\leq10^9\)),若\(dis(x,y)=點x到點y的簡單路徑上有幾條邊\)
    那么求對于所有滿足\(dis(x,y)=dis(y,z)=dis(x,z)且x\neq y且y\neq z 且 z\neq x\)的三元組\((x,y,z)\)\(w_x*w_y+w_y*w_z+w_z*w_x\)的和是多少

    題解

    暴力的dp:
    \(f_0(u,i)\)表示點\(u\)的子樹中所有點\(v\)滿足\(dis(u,v)=k\)的點的個數
    \(f_1(u,i)\)表示點\(u\)的子樹中所有點\(v\)滿足\(dis(u,v)=k\)的點的點權和
    \(g_0(u,i)\)表示點\(u\)的子樹中所有點對\((x,y)\)滿足\(dis(u,x)=dis(u,y)=dis(x,y)-k\)\(x\neq y\)的點對的\(點對點權和\)之和
    \(g_0(u,i)\)表示點\(u\)的子樹中所有點對\((x,y)\)滿足\(dis(u,x)=dis(u,y)=dis(x,y)-k\)\(x\neq y\)的點對的\(點對點權積\)之和
    \(ans(u)\)表示以點\(u\)為LCA的三元組(x,y,z)對答案造成的貢獻
    則有:
    \(\space\bf f_0(u,i)=\sum_{v\in son(u)}{f_0(v,i-1)}(i>0)\)
    \(u\)的兒子們的子樹中到點\(u\)的兒子的距離為\(i-1\)的點顯然到點\(u\)的距離為\(i\)
    \(\space\bf f_0(u,0)=1\)
    \(u\)的子樹中滿足\(dis(u,v)=0\)的點只有\(u\)自己
    \(\space\bf f_1(u,i)=\sum_{v\in son(u)}{f_1(v,i-1)}(i>0)\)
    \(f_0\)
    \(\space\bf f_1(u,0)=w_u\)
    \(f_0\)
    \(\space\bf g_0(u,i)=\sum_{v\in son(u)}{g_0(v,i+1)+f_1(u,i)*f_0(v,i-1)+f_1(v,i-1)*f_0(u,i)}(i>0)\)
    兩個點都在v的子樹內時,\(點對點權和\)之和為\(g_0(v,i+1)\);選的第一個點在其他兒子的子樹中,另一個在v的子樹中時,“可以作為第一個點的點”的點權和為\(f_1(u,i)\),與之對應的點有\(f_0(v,i-1)\)種取法,因此選的第一個點的貢獻總和是\(f_1(u,i)*f_0(v,i-1)\),另一個點同理,貢獻為\(f_1(v,i-1)*f_0(u,i)\)
    \(\space\bf g_0(u,0)=\sum_{v\in son(u)}{g_0(v,1)}\)
    這些點對\((x,y)\)都滿足\(dis(u,x)=dis(u,y)=dis(x,y)\)\(dis(u,LCA(x,y))=dis(x,LCA(x,y))=dis(y,LCA(x,y))\),又因為\(x\neq y\),所以\(dis(u,LCA(x,y))\neq0\),也就是說點對\((x,y)\)中的兩點一定在\(u\)的同一個兒子的子樹,那就直接從兒子的\(g_0\)轉移就行了
    \(\space\bf g_1(u,i)=\sum_{v\in son(u)}{g_1(v,i+1)+f_1(u,i)*f_1(v,i-1)}(i>0)\)
    兩個點都在v的子樹內時,同\(g_0\);選的第一個點在其他兒子的子樹中,另一個在v的子樹中時,“可以作為第一個點的點”的點權和為\(f_1(u,i)\),“可以作為另一個點的點”的點權和為\(f_1(v,i-1)\),將它們相乘就行了
    \(\space\bf g_1(u,0)=\sum_{v\in son(u)}{g_1(v,1)}\)
    \(g_0\)
    \(\space\bf ans(u)=\sum_{k}\sum_{v\in son(u)}{g_1(v,k+1)*f_0(u,k)+g_0(v,k+1)*f_1(v,k)+g_1(u,k+1)*f_0(v,k)+g_0(u,k+1)*f_1(v,k)}\)
    兩個點\((x,y)\)\(v\)的子樹中,一個點(z)在外面時,\(x,y\)所有取法的點權積之和是\(g_1(v,k+1)\),可以與\(f_0(u,k)\)\(z\)相匹配,所以所有的\(x*y\)之和為\(g_1(v,k+1)*f_0(u,k)\)\(x,y\)所有取法的點權和之和是\(g_0(v,k+1)\),可以與之相乘的\(z\)的和為\(f_1(u,k)\),所以所有的\(x*z+y*z\)之和為\(g_0(v,k+1)*f_1(v,k)\);兩個點\((x,y)\)在外面,一個點\((z)\)\(v\)的子樹中時,同理,所有的\(x*y\)之和為\(g_1(u,k+1)*f_0(v,k)\),所有的\(x*z+y*z\)之和為\(g_0(u,k+1)*f_1(v,k)\)
    ----------------暴力與不暴力的分割線-------------
    會發現合并每個點\(u\)和它的第一個兒子時,相當于把這個兒子的\(f,g\)賦給\(u\),只不過\(f\)向后錯了一位,\(g\)向前錯了一位
    合并\(u\)與后面的兒子時,設\(dep(x)\)為“點\(x\)的子樹中到點\(x\)最遠的點”與點\(x\)的距離,那么合并\(u\)與它的兒子\(v\)的時間復雜度為\(\Theta(dep(v)))\),求\(u\)的兒子\(v\)\(ans(u)\)的貢獻的復雜度也是這個
    那么就可以考慮對這棵樹進行長鏈剖分,每次將\(u\)的長兒子的\(f,g\)\(u\)(是“直接給”,而不是“用一個循環賦值”!這樣這一步的時間復雜度就是\(\Theta(1)\)了),再將\(u\)與它的每個短兒子合并(這一步的時間復雜度是\(\Theta(\sum_{v\in son(u)}{dep(v)})\)),總時間復雜度可以看成只有所有長鏈的鏈頂會對總時間復雜度有貢獻,\(總時間復雜度=\Theta(\sum{鏈長})=\Theta(n)\)
    其中關鍵的一步,“將\(u\)的長兒子的\(f,g\)\(u\)”可以用指針實現,不過并不對勁的人并不會使用指針,那就可以將\(f(u,i)\)放到第\(dfn[u]+i\)\(dfn[u]\)表示先走長兒子的dfs序)的位置,將\(g(u,i)\)放到\(dfn[top[u]]+dep(u)+i\)\(dep(x)\)還是表示“點\(x\)的子樹中到點\(x\)最遠的點”與點\(x\)的距離)的位置,只不過略有麻煩

    代碼
    “略有”麻煩?
    不使用指針的做法,關鍵在于要設計出兩個映射\(F(x,i),G(x,i)\),使\(F(i,k)=F(長兒子(i),k-1),G(i,k)=G(長兒子(i),k+1)\)
  • #include<algorithm> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define rep(i,x,y) for(register int i=(x);i<=(y);++i) #define dwn(i,x,y) for(register int i=(x);i>=(y);--i) #define view(u,k) for(int k=fir[u];k!=-1;k=nxt[k]) #define LL long long #define maxn 2000010 #define maxm (maxn<<1) #define F(x,i) (dfn[x]+i) #define G(x,i) ((dfn[top[x]]<<1)+dep[x]+i) using namespace std; int read() {int x=0,f=1;char ch=getchar();while(!isdigit(ch)&&ch!='-')ch=getchar();if(ch=='-')f=-1,ch=getchar();while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();return x*f; } void write(int x) {if(x==0){putchar('0'),putchar('\n');return;}int f=0;char ch[20];if(x<0)putchar('-'),x=-x;while(x)ch[++f]=x%10+'0',x/=10;while(f)putchar(ch[f--]);putchar('\n');return; } const LL mod=998244353; int n,fir[maxn],nxt[maxm],v[maxm],dep[maxn],dfn[maxn],tim,son[maxn],fa[maxn],top[maxn],cnt; int f[2][maxn],g[2][maxm],w[maxn],ans,col[maxn],num[maxn]; void ade(int u1,int v1){v[cnt]=v1,nxt[cnt]=fir[u1],fir[u1]=cnt++;} void getdep(int u) {view(u,k)if(v[k]!=fa[u]){fa[v[k]]=u,getdep(v[k]),dep[u]=max(dep[u],dep[v[k]]+1);if(!son[u]||dep[v[k]]>dep[son[u]])son[u]=v[k];} } void getdfn(int u,int anc) {top[u]=anc,dfn[u]=++tim;if(son[u])getdfn(son[u],anc);view(u,k)if(v[k]!=son[u]&&v[k]!=fa[u])getdfn(v[k],v[k]); } void getans(int u) {if(son[u])getans(son[u]);view(u,k)if(v[k]!=fa[u]&&v[k]!=son[u]){getans(v[k]);rep(i,0,dep[v[k]]){ans=((LL)ans+(LL)g[1][G(v[k],i+1)]*(LL)f[0][F(u,i)]%mod+(LL)g[0][G(v[k],i+1)]*(LL)f[1][F(u,i)]%mod)%mod,ans=((LL)ans+(LL)g[1][G(u,i+1)]*(LL)f[0][F(v[k],i)]%mod+(LL)g[0][G(u,i+1)]*(LL)f[1][F(v[k],i)]%mod)%mod;}g[0][G(u,0)]=(g[0][G(u,0)]+g[0][G(v[k],1)])%mod,g[1][G(u,0)]=(g[1][G(u,0)]+g[1][G(v[k],1)])%mod; rep(i,1,dep[v[k]]+1){g[0][G(u,i)]=((LL)g[0][G(u,i)]+(LL)g[0][G(v[k],i+1)]+(LL)f[1][F(u,i)]*(LL)f[0][F(v[k],i-1)]%mod+(LL)f[1][F(v[k],i-1)]*(LL)f[0][F(u,i)]%mod)%mod,g[1][G(u,i)]=((LL)g[1][G(u,i)]+(LL)g[1][G(v[k],i+1)]+(LL)f[1][F(u,i)]*(LL)f[1][F(v[k],i-1)]%mod)%mod;f[0][F(u,i)]=(f[0][F(u,i)]+f[0][F(v[k],i-1)])%mod,f[1][F(u,i)]=(f[1][F(u,i)]+f[1][F(v[k],i-1)])%mod;}}f[0][F(u,0)]=1,f[1][F(u,0)]=w[u],ans=((LL)ans+(LL)g[1][G(u,0)]+(LL)g[0][G(u,0)]*(LL)w[u]%mod)%mod;g[1][G(u,0)]=g[0][G(u,0)]=0; } int main() {freopen("tree.in","r",stdin);freopen("tree.out","w",stdout);memset(fir,-1,sizeof(fir));n=read();rep(i,1,n-1){int x=read(),y=read();ade(x,y),ade(y,x);}rep(i,1,n)w[i]=read();getdep(1),getdfn(1,1),getans(1);write(ans);return 0; } /* 10 2 1 3 2 4 3 5 3 6 3 7 6 8 7 9 5 10 3 6 6 9 8 9 5 3 4 10 2 */ //1143

    轉載于:https://www.cnblogs.com/xzyf/p/10474515.html

    總結

    以上是生活随笔為你收集整理的并不对劲的长链剖分的全部內容,希望文章能夠幫你解決所遇到的問題。

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