【bzoj4543】Hotel加強版(thr)
Portal --> bzoj4543
Solution
? 一年前的題== 然而一年前我大概是在劃水qwq
?? 其實感覺好像關鍵是。。設一個好的狀態?然後。。你要用一種十分優秀的方式快樂轉移qwq
?
?? 首先是狀態,我們設\(f[x][i]\)表示以\(x\)為根的子樹中,與\(x\)的距離為\(i\)的節點數量,\(g[x][i]\)表示。。\(x\)子樹內形如下圖的點對\((a,b)\)(無序)的數量
?? 其中\(d\)是一個。。不確定的值(說白了就是所有的\(d\)都要考慮到),或者更加直白地說,只要在\(x\)節點上“接上”一條長度為\(i\)的邊(邊連著一個節點\(y\)
?? 然後這個時候我們就可以快樂轉移了,枚舉\(x\)的後繼\(u\):
\[
\begin{aligned}
g[x][i]&+=f[x][i]*f[u][i-1]+g[u][i+1]\f[x][i]&+=f[u][i-1]\\end{aligned}
\]
?? 其中\(i\)的範圍都是\(1\sim\)子樹內的最大深度
?? 稍微解釋一下\(g\)的轉移:前面的乘法的話就是\(lca\)為\(x\)的情況,後面的話就是從子樹中直接繼承上來(註意\(+1\)的話是因為\(i\)是被減的那個量)
?? 接下來是答案的統計,考慮統計經過\(x\)
\[ ans+=\sum\limits_{u\in son(x)}f[u][i-1]*g[x][i]+g[u][i]*f[x][i-1] \]
?? 同樣的\(i\)的範圍也是\(1\sim\)子樹內的最大深度
?? 那麽這個時候可能會有疑問,接上去的那個點的連邊不能是一個帶“折”的邊嗎,為什麽可以直接用不帶折的\(f\)算呢?其實稍微畫一下圖:
?? (註意圖中的一條邊。。可能是一條鏈只是我懶得畫了qwq)我們會發現這種情況其實會在統計圖中那個沒有標號的節點的時候統計到(沒有標號的節點的\(g\)中是可能包含了\((a,b)\)這種情況的(如果他們可能構成一種合法方案的話),然後這個時候\(y\)
?? (所以說其實。。也不能夠叫做統計經過\(x\)的方案的答案)
?
? 然後現在的問題就是我們怎麽快速轉移
?? 發現那個\(i\)的變化很有規律,考慮當我們在樹形dp中將第一個兒子的值拿來更新父親的時候的情況,稍微寫一下大概是這樣:
\[
\begin{aligned}
f[x][i]&=f[u][i-1]\g[x][i]&=g[u][i+1]\\end{aligned}
\]
?? 我們將\(f[x],g[x],f[u],g[u]\)都看成一個整體的話,\(f[u]\)到\(f[x]\)的轉移其實相當於整體向前或向後偏移了一位,\(g[x]\)和\(g[u]\)的轉移也是一樣,所以我們其實可以用指針(或者。。下標偏移一下什麽的,我的代碼寫的就是這個因為我不太會用指針qwq)來實現\(O(1)\)的轉移
?? 然後對於後面的情況我們都要老老實實枚舉\(i\),因此轉移復雜度是跟\(u\)的子樹最大深度相關的
?? 所以我們不妨考慮把子樹最大深度最大的那個後繼欽定為第一個後繼,\(O(1)\)轉移它的信息,然後其他的後繼就\(O(dep)\)轉移(其實就是長鏈剖分一下qwq把重兒子擺在一起),這樣的話就能夠做到整體復雜度\(O(n)\)了(證明的話我不是很會qwq網上看到dalao的博客中說的貌似是直接用深度相減算出一個點的轉移的復雜度然後求一下和發現除了葉子節點以外的所有點的\(dep\)都被抵消了所以就是\(O(n)\)了)
?? 然後再稍微說一下下標偏移寫法的實現:具體一點就是我們考慮將所有的\(f\)的值排成一排(\(g\)的值也是一樣),像樹鏈剖分中的線段樹一樣將一條長鏈中所有的節點的信息放在連續的一段,鏈上的轉移的話\(f\)相當於每次往後移一位,\(g\)相當於每次往前移一位(因此在存的時候一條鏈預留的長度應該\(*2\),因為要預留往前移的空間)
?? 轉移的時候一定要註意邊界。。。
?
?? 代碼大概長這個樣子
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=5*(1e5)+10;
struct xxx{
int y,nxt;
}a[N*2];
int h[N],dep[N],top[N],mx[N],son[N],pre[N],pos[N];
int dfn[N];
ll f[N*10],g[N*10];
int n,m,tot,dfn_t,lis_pos;
ll ans;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int fa,int x,int d){
int u;
dep[x]=d; mx[x]=1;
pre[x]=fa; son[x]=0;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
mx[x]=max(mx[x],mx[u]+1);
if (mx[son[x]]<mx[u]+1) son[x]=u;
}
}
void dfs1(int fa,int x){
int u;
dfn[x]=++dfn_t;
if (son[x]){
top[son[x]]=top[x];
dfs1(x,son[x]);
}
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||u==son[x]) continue;
top[u]=u; pos[u]=lis_pos+mx[u];
lis_pos+=mx[u]*2;
dfs1(x,u);
}
}
int locf(int x,int i){return dfn[x]+i;}
int locg(int x,int i){return pos[top[x]]-(dep[x]-dep[top[x]])+i;}
int calc(int x,int u){
for (int i=1;i<=mx[u];++i)
ans+=f[locf(u,i-1)]*g[locg(x,i)];
for (int i=1;i<mx[u];++i)
ans+=g[locg(u,i)]*f[locf(x,i-1)];
for (int i=0;i<mx[u]-1;++i)
g[locg(x,i)]+=g[locg(u,i+1)];
for (int i=1;i<=mx[u];++i){
g[locg(x,i)]+=f[locf(x,i)]*f[locf(u,i-1)];
f[locf(x,i)]+=f[locf(u,i-1)];
}
}
void solve(int fa,int x){
int u;
if (!son[x]){
f[locf(x,0)]=1; g[locg(x,0)]=0;
return;
}
solve(x,son[x]);
g[locg(x,mx[x])]=g[locg(x,mx[x]-1)]=0;
ans+=g[locg(x,0)];
f[locf(x,0)]=1;
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa||u==son[x]) continue;
solve(x,u);
calc(x,u);
}
}
void init(){
memset(h,-1,sizeof(h));
tot=0;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y;
while (1){
scanf("%d",&n);
if (!n) return 0;
init();
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(0,1,1);
dfn_t=0; lis_pos=0;
top[1]=1; pos[1]=lis_pos+mx[1];
lis_pos=mx[1]*2;
dfs1(0,1);
ans=0;
solve(0,1);
printf("%lld\n",ans);
}
}
【bzoj4543】Hotel加強版(thr)