題目
題目大意
給你一棵樹,對於每一條邊,求刪去這條邊之後,再用一條邊(自己定)連接兩個連通塊,形成的樹的直徑最小是多少。
正解
首先,將這棵樹的直徑給找出來。顯然,如果刪去的邊不在直徑上,那麼答案就是直徑。
接下來考慮刪去的邊在直徑上的情況。
自己連的邊應該要是兩棵樹的直徑的中點(中點就是直徑上到端點最大距離最小的點)。
答案就是兩棵樹的直徑的一半(當然這是粗略的說法)加上邊權,和兩棵樹內部的直徑長度的最大值。
設直徑端點爲和,現在想象直徑是橫過來的一條線,有一堆樹掛在上面。
在直徑上從左到右枚舉刪去哪條邊,順帶着維護中點在哪裏。
有個結論:中點肯定在原來的直徑上。
(後面都以的一邊爲例,顯然另一邊是一樣的)
反證法,設中點爲,不在直徑上。設爲到路徑上第一個出現在直徑上的點。
現在找最遠的點。
如果在子樹之外,那麼路徑就是到和到的距離。這時候如果要使到最大,則。這時候將變成更優。
如果在子樹之內,那麼到的距離比到的距離長,與假設矛盾。
如果在子樹之內,在子樹之外,那麼到的距離比到的距離長,矛盾。
接下來考慮如何維護直徑。
在原來的直徑上,對於每個節點,預處理出表示子樹中最遠點到的長度。
設爲到的距離。
顯然,新的直徑的一個端點是。直徑可以分成在原來直徑上和在某棵子樹內的兩段。
設爲直徑的拐點,則直徑的長度爲
設爲直徑的中點,則直徑一半的長度(形象的說法)爲
現在被刪去的邊在原來的直徑上從左往右移動,每個拐點都能搞出一條路徑。在這些路徑中找長度最大的,作爲直徑,然後移動到最小的地方,這時候就求出來了。
在這個過程中,我們發現只會從向移動。
所以直接做就可以了(題解說要單調隊列,但實際上完全不用。具體見代碼。)
代碼
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2000010
#define ll long long
int n;
struct EDGE{
int to,w,num;
EDGE *las;
} e[N*2],*last[N];
int ne;
unsigned long long num;
unsigned long long get(){
num^=(num<<13);
num^=(num>>17);
num^=(num<<5);
return num;
}
void gen(){
int B,D;
scanf("%d%llu%d%d",&n,&num,&B,&D);
for(int i=2;i<=n;i++){
int a=get()%min(i-1,B)+i-min(i-1,B),b=get()%D;
e[ne]={a,b,i-1,last[i]};
last[i]=e+ne++;
e[ne]={i,b,i-1,last[a]};
last[a]=e+ne++;
}
}
ll ans[N];
int q[N];
ll ds[N],dt[N],alen;
int S,T,pre[N],suc[N];
EDGE *et[N];
void init(){
static int vis[N];
int BZ,h,t;
vis[1]=BZ=1;
q[h=t=1]=1;
ll *dis=ds;
dis[1]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (vis[y]!=BZ){
vis[y]=BZ;
dis[y]=dis[x]+ei->w;
q[++t]=y;
}
}
}
S=1;
for (int i=1;i<=n;++i)
if (dis[i]>dis[S])
S=i;
q[h=t=1]=S;
vis[S]=++BZ;
dis[S]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (vis[y]!=BZ){
vis[y]=BZ;
dis[y]=dis[x]+ei->w;
pre[y]=x;
et[y]=ei;
q[++t]=y;
}
}
}
T=S;
for (int i=1;i<=n;++i)
if (dis[i]>dis[T])
T=i;
for (int i=T;i!=S;i=pre[i])
suc[pre[i]]=i;
suc[T]=0;
for (int i=1;i<=n;++i)
if (!suc[i] && i!=T)
pre[i]=0;
}
ll f[N];
int fa[N];
void dp1(int rt){
int h,t;
q[h=t=1]=rt;
fa[rt]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=pre[rt] && y!=suc[rt] && y!=fa[x]){
ans[ei->num]=alen;
fa[y]=x;
q[++t]=y;
}
}
}
for (int i=t;i>=1;--i){
int x=q[i],y;
f[x]=0;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=pre[rt] && y!=suc[rt] && y!=fa[x])
f[x]=max(f[x],f[y]+ei->w);
}
}
}
ll gs[N],gt[N];
void dp2(int rt,int *cant,ll *g){
int h,t;
q[h=t=1]=rt;
fa[rt]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=cant[x] && y!=fa[x]){
fa[y]=x;
q[++t]=y;
}
}
}
for (int i=t;i>=1;--i){
int x=q[i],y;
ll fmx=0,smx=0;
f[x]=0;
g[x]=0;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=cant[x] && y!=fa[x]){
g[x]=max(g[x],g[y]);
if (f[y]+ei->w>fmx)
smx=fmx,fmx=f[y]+ei->w;
else if (f[y]+ei->w>smx)
smx=f[y]+ei->w;
}
}
f[x]=fmx;
g[x]=max(g[x],fmx+smx);
}
}
ll hs[N],ht[N];
void calc(int beg,int end,int *nxt,ll *h,ll *dis){
int a=beg,mx=beg;
h[beg]=f[beg];
for (int x=nxt[beg];x!=end;x=nxt[x]){
if (dis[x]+f[x]>dis[mx]+f[mx]){
mx=x;
while (a!=x && max(dis[nxt[a]],dis[mx]+f[mx]-dis[nxt[a]])<max(dis[a],dis[mx]+f[mx]-dis[a]))
a=nxt[a];
}
h[x]=max(dis[a],dis[mx]+f[mx]-dis[a]);
}
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
gen();
init();
alen=ds[T];
dp2(T,suc,gs),dp2(S,pre,gt);
for (int i=S;i;i=suc[i])
dt[i]=alen-ds[i],dp1(i);
calc(S,T,suc,hs,ds);
calc(T,S,pre,ht,dt);
for (int i=S;i!=T;i=suc[i])
ans[et[suc[i]]->num]=max(max(gs[i],gt[suc[i]]),hs[i]+ht[suc[i]]+et[suc[i]]->w);
ll s=0;
for (int i=1;i<n;++i)
s^=ans[i]%998244353*i%998244353;
printf("%lld\n",s);
return 0;
}
總結
論猜結論的重要性……