花花的森林

2021-07-24 08:00:38 字數 3875 閱讀 3620

題目描述

花花有一棵帶n 個頂點的樹t,每個節點有乙個點權ai。

有一天,他認為擁有兩棵樹更好一些。所以,他從t 中刪去了一條邊。

第二天,他認為三棵樹或許又更好一些。因此,他又從他擁有的某一棵樹中去除了一條邊。

如此往復。每一天,花花都會刪去一條尚未被刪去的邊,直到他得到了乙個包含了n 棵只有乙個點的樹的森林。

定義一條簡單路徑的權值為路徑上點權之和,一棵樹的直徑為樹上權值最大的簡單路徑。

花花認為樹最重要的特徵就是它的直徑。所以他想請你算出任一時刻他擁有的所有樹的直徑的乘積。因為這個數可能很大,他要求你輸出乘積對109

+7取模之後的結果。

輸入 輸入的第一行包含乙個整數n,表示樹t 上頂點的數量。

下一行包含n 個空格分隔的整數ai,表示頂點的權值。

之後的n-1 行中,每一行包含兩個用空格分隔的整數xi 和yi,表示節點xi 和yi 之間連有一條邊,編號為i。

再之後n-1 行中,每一行包含乙個整數kj,表示在第j 天裡會被刪除的邊的編號

輸出 輸出n 行。

在第i 行,輸出刪除i-1 條邊之後,所有樹直徑的乘積對10^9 + 7 取模的結果。

樣例輸入

3 1 2 3

1 2

1 3

2 1

樣例輸出

6 9

6 提示

初始,樹的直徑為6(由節點2、1 和3 構成的路徑)。在第一天之後,得到了兩棵直徑都為3 的樹。第二天之後,得到了三棵直徑分別為1,2,3 的樹,乘積為6。

• 對於40% 的資料:

n<=

100 ;

• 另有20% 的資料:

n<=

1000

; • 另有20% 的資料:

n<=104

; • 對於100% 的資料:

n<=105

;ai<=104

。根據一貫的套路,這種刪除邊的問題很多都是倒著添邊做的。

那我們就來考慮倒著做

最後樹成了n個點的森林

每次合併兩棵樹,除去原來兩棵樹的直徑,乘上合併後的新直徑

除直徑用乘逆元

那麼現在的問題就是:如何快速得出兩棵樹合併後產生的新直徑。

考試的時候,我這個渣渣當然不會了,於是我就暴力更新一條鏈,極限接近n2

,但快得出奇,100000的隨機資料都只需要0.8s

其實,關於直徑又有乙個套路,當兩棵子樹合併的時候,x樹的直徑兩端點是a b,y樹的直徑兩端點是c d,則新直徑必定是abcd的某個點對。

求路徑用lca

結果我發現正解比暴力慢了1倍

暴力

#include

#include

#include

#include

#define ll long long

using namespace std;

const ll mod=1e9+7;

int n,x,y,u,tot;

int st[100005],ed[100005],a[100005],cut[100005],f[100005];

int s[100005],fa[100005],d[100005];

int head[100005],next[200005],to[200005];

ll now,ans[100005];

void dfs(int k,int pre)

void add(int x,int y)

int get(int x)

void update(int k)

else

if(s[to[i]]>s2) s2=s[to[i]];

d[k]=max(d[k],d[to[i]]);

}s[k]=s1+a[k];

d[k]=max(d[k],s1+s2+a[k]);

if(k!=u) update(fa[k]); else

return;

}ll ny(ll x,ll y)

return p;

}void prepare()

for(int i=1;i"%d%d",&st[i],&ed[i]);

add(st[i],ed[i]);

add(ed[i],st[i]);

}dfs(1,0);

for(int i=1;i"%d",&cut[i]);

tot=0;

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

ans[n]=1;

for(int i=1;i<=n;i++) ans[n]=(ans[n]*a[i])%mod;

now=ans[n]; //現在的直徑乘積

}

int main()

else

ans[i]=now;

}for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);

return

0;}

正解

#include

#include

#include

#include

#define ll long long

using namespace std;

const ll mod=1e9+7;

int n,x,y,ty,i1,i2,dis,dad,tot;

int u[100005],v[100005],b[5];

int st[100005],ed[100005],a[100005],cut[100005];

int fa[100005][20],d[100005],f[100005],s[100005];

int head[100005],next[200005],to[200005],deep[100005];

ll now,ans[100005];

void dfs(int k,int pre)

void add(int x,int y)

int get(int x)

ll ny(ll x,ll y)

return p;

}void prepare()

for(int i=1;i"%d%d",&st[i],&ed[i]);

add(st[i],ed[i]);

add(ed[i],st[i]);

}dfs(1,0);

for(int i=1;(1

for(int i=1;i"%d",&cut[i]);

tot=0;

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

ans[n]=1;

for(int i=1;i<=n;i++) ans[n]=(ans[n]*a[i])%mod;

now=ans[n]; //現在的直徑乘積

}int lca(int x,int y)

int main()

}now=(now*ny((ll)(d[x]),mod-2))%mod;

now=(now*ny((ll)(d[ty]),mod-2))%mod;

d[ty]=dis,u[ty]=i1,v[ty]=i2;

now=(now*d[ty])%mod;

}else

}now=(now*ny((ll)(d[y]),mod-2))%mod;

now=(now*ny((ll)(d[ty]),mod-2))%mod;

d[ty]=dis,u[ty]=i1,v[ty]=i2;

now=(now*d[ty])%mod;

}ans[i]=now;

}for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);

return

0;}

花花的奶茶店

眾所周知,花花學長說自己要 從此要杜絕奶茶。但是某一天,花花學長聽說新開了一家奶茶店,他決定只是去奶茶店康一康。他初始在數字線上的點n 0 n 100,000 而奶茶店在同一數字線上的點k 0 k 100,000 花花學長有兩種移動方式 步行和傳送。行走 花花學長可以花費一分鐘從任意點x移至點x 1...

花花的禮物 huahua

from dtoj 1936 花花是個愛動腦子的孩子,在她的生日的時候,她的爸爸給她準備了個禮物。但是,她的爸爸並不想讓她輕易得到禮物,他把禮物放在了乙個箱子裡面,只有輸入正確的密碼才能開啟箱子,而她的爸爸告訴了她這個禮物該怎麼得到 他們所在的城市可以看成有 nn n 個點,mm m 條邊的無向圖,...

有心插花花不開

其實吧,標題是亂寫的。今天去上班,不知道是吃了魚腥草藥的原因,還是因為小感冒的原因,一整天反應遲頓。結果,領導們決定讓我出差 替交付出差。我表示我不太熟悉那些東西,領導說沒關係,會有另外乙個同事一塊去。那我就沒意見了。結果本來想去跟這哥們聊一聊的時候,發現他在填離職申請書。忽然感覺自己就是一小把戲。...