2651 城市改建 樹形DP

2021-07-11 10:17:30 字數 4846 閱讀 9065

我太sb了。。一看輸出方案就瞎jb記錄了一坨資訊。。最後發現根本沒有用。。

結果寫了6.7k。。。成功成為了bzoj寫的最長跑的最慢的選手2333。。

題目即在一棵樹上刪一邊加一邊,使得新樹的直徑最小。

那麼我們就要維護直徑相關的資訊。。於是大力dp。。

首先自底向上dp,設fi

表示以節點

i 為根的子樹的直徑,gi

表示以節點

i 為根的子樹中離

i最遠的點的距離。 假設j

,k為i

的孩子,且j≠

k那麼有 gi

=max

fi=max

然後我們自頂向下dp,類似的,設hi

表示除去以

i 為根的子樹後,剩餘樹的直徑,pi

表示除去以

i 為根的子樹後,離

i最遠點的距離。 設j

,k 是i

的兄弟,且j≠

k那麼有pi

=max

hi=max

然後就記錄一些字首max字尾max最大次大什麼的轉移就行了。

至於記錄方案。。只要把f,

g 相關的轉移記錄下來就好了,至於p,

h 根本用不到。多寫了一坨懶得刪了。

然後我們列舉刪去某條邊(i

,fai

) ,那麼有 an

s=ma

x 最後b

fs一下記錄方案。

時間複雜度o(

n)。附sb**:

#include

#include

#include

#include

#include

using namespace std;

const int n=300005;

const int inf=1000000007;

int n,cnt,ans,id;

int head[n],list[n<<1],next[n<<1];

int premx[n],sufmx[n],preid[n],sufid[n],fa[n],f[n],g[n],h[n],p[n],q[n];

pair pref[n],preh[n];

int preg[n],prep[n],pre[n],path[n];

bool vis[n];

inline int

read()

while (c>='0'&&c<='9')

return a*f;

}inline void insert(int

x,int

y)inline int getans(int i)

inline void clear(int tot)

void dp1(int

x) int mx1=-inf,mx2=-inf,j1,j2;

for (int i=head[x];i;i=next[i])

if (list[i]!=fa[x])

// f[x]=max(f[x],g[x]);

if (g[x]>f[x]) f[x]=g[x],pref[x]=make_pair(preg[x],x);

if (mx1!=-inf&&mx2!=-inf)

// f[x]=max(f[x],mx1+mx2+2);

if (mx1+mx2+2>f[x])

f[x]=mx1+mx2+2,pref[x]=make_pair(j1,j2);

}void dp2(int

x) clear(tot);

for (int i=1;i<=tot;i++)

// premx[i]=max(premx[i-1],f[q[i]]);

if (f[q[i]]>premx[i-1])

premx[i]=f[q[i]],preid[i]=q[i];

else premx[i]=premx[i-1],preid[i]=preid[i-1];

for (int i=tot;i;i--)

// sufmx[i]=max(sufmx[i+1],f[q[i]]);

if (f[q[i]]>sufmx[i+1])

sufmx[i]=f[q[i]],sufid[i]=q[i];

else sufmx[i]=sufmx[i+1],sufid[i]=sufid[i+1];

for (int i=2;i<=tot;i++)

// h[q[i]]=max(h[q[i]],premx[i-1]);

if (premx[i-1]>h[q[i]])

h[q[i]]=premx[i-1],preh[q[i]]=pref[preid[i-1]];

for (int i=1;iq[i]]=max(h[q[i]],sufmx[i+1]);

if (sufmx[i+1]>h[q[i]])

h[q[i]]=sufmx[i+1],preh[q[i]]=pref[sufid[i+1]];

clear(tot);

for (int i=1;i<=tot;i++)

// premx[i]=max(premx[i-1],g[q[i]]);

if (g[q[i]]>premx[i-1]) premx[i]=g[q[i]],preid[i]=q[i];

else premx[i]=premx[i-1],preid[i]=preid[i-1];

for (int i=tot;i;i--)

// sufmx[i]=max(sufmx[i+1],g[q[i]]);

if (g[q[i]]>sufmx[i+1]) sufmx[i]=g[q[i]],sufid[i]=q[i];

else sufmx[i]=sufmx[i+1],sufid[i]=sufid[i+1];

for (int i=2;i<=tot;i++)

// p[q[i]]=max(p[q[i]],premx[i-1]+2);

if (premx[i-1]+2>p[q[i]])

p[q[i]]=premx[i-1]+2,prep[q[i]]=preg[preid[i-1]];

for (int i=1;iq[i]]=max(p[q[i]],sufmx[i+1]+2);

if (sufmx[i+1]+2>p[q[i]])

p[q[i]]=sufmx[i+1]+2,prep[q[i]]=preg[sufid[i+1]];

if (tot==1)

// h[q[1]]=max(h[q[1]],p[x]);

if (p[x]>h[q[1]])

h[q[1]]=p[x],preh[q[1]]=make_pair(prep[x],x);

for (int i=2;i<=tot;i++)

// h[q[i]]=max(h[q[i]],p[x]+premx[i-1]+1);

if (p[x]+premx[i-1]+1>h[q[i]])

h[q[i]]=p[x]+premx[i-1]+1,preh[q[i]]=make_pair(preg[preid[i-1]],prep[x]);

for (int i=1;iq[i]]=max(h[q[i]],p[x]+sufmx[i+1]+1);

if (p[x]+sufmx[i+1]+1>h[q[i]])

h[q[i]]=p[x]+sufmx[i+1]+1,preh[q[i]]=make_pair(preg[sufid[i+1]],prep[x]);

for (int i=2;iq[i]]=max(h[q[i]],premx[i-1]+sufmx[i+1]+2);

if (premx[i-1]+sufmx[i+1]+2>h[q[i]])

h[q[i]]=premx[i-1]+sufmx[i+1]+2,preh[q[i]]=make_pair(preg[preid[i-1]],preg[sufid[i+1]]);

int mx1,mx2,j1,j2;

mx1=-inf; mx2=-inf;

for (int i=1;i<=tot;i++)

mx1=-inf; mx2=-inf;

for (int i=tot;i;i--)

for (int i=1;i<=tot;i++) dp2(q[i]);

}inline void bfs(int

x) }

}inline int find(int

x,int ff)

// cout << endl;

return i;

}int main()

dp1(1); dp2(1);

//for (int i=1;i<=n;i++) cout << p[i] <<" ";

// cout << endl;

ans=inf;

for (int i=1;i<=n;i++)

// ans=min(ans,max(max((f[i]+1)/2+(h[i]+1)/2+1,f[i]),h[i]));

if (getans(i)int a=id,b=fa[id];

memset(fa,0,sizeof(fa));

memset(f,0,sizeof(f));

memset(g,0,sizeof(g));

printf("%d\n%d

%d\n%d

%d\n",ans,a,b,find(a,b),find(b,a));

//for (int i=1;i<=n;i++)

// cout << i << " "

<< fa[i] << endl;

return

0;}

BZOJ 2651 城市改建 樹形DP 模擬?

給一顆樹,刪除一條邊再加一條邊,使它仍為一顆樹且任意兩點間的距離的最大值最小。題目資料範圍描述有問題,n為1或重建不能使任意兩點距離最大值變小,可以輸出任意答案。刪除一條邊後會使它變成兩顆樹,兩棵樹的直徑的中點相連一定是使距離最小的 紅色的邊為刪除重建的邊 在樹上dp維護每個子樹的最大直徑 h x ...

樹形dp 7 14城市

很典型的按照邊考慮貢獻的題。小a居住的城市可以認為由n個街區組成。街區從1到n依次標號街區與街區之間由街道相連,每個街區都可以通過若干條街道到達任意乙個街區,共有n 1條街道。其中標號為i的街區居住了i名居民。居民會去拜訪別人,但是要花費dis u,v 的過路費,u是他所在的城市,v是他拜訪的人所在...

TJOI2017 城市 樹形dp

這是個神仙題,會卡常 題目讓你改一條邊把直徑變得最短。列舉每條邊,會把圖分成兩個地方,兩個連通塊 x區和y區域 都換根dp一下,算出離x最遠的點的距離記為dis x 然後列舉一下 新直徑有三個 1 max dis x x裡面最大的 2 dis y y裡面最大的 3 min dis x dis y l...