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