我太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...