hdu多校第九場 1005(hdu-6430:TeaTree)
阿新 • • 發佈:2019-02-04
題目大意:
對於一個結點 x,它的值可以是當前子樹內任意兩點的gcd值。但是要求 lca(i,j)=x。
最後問你每個結點最大的值可能是多少,如果不存在輸出-1。
解題思路:
本來想用樹上dsu來寫的,結果寫了一上午,總感覺dsu有哪些地方不對勁,開始刪刪改改,最後發現寫出來了一個暴力合併,複雜度的話不太會證明,可能因為約數較少的緣故,所以複雜度不是很高。
具體實現就是預先打出每個結點的約數,用vector儲存起來。儲存以後直接dfs,對於當前結點,把它的所有兒子遍歷完之後,我們使用一個bitset把當前結點的約數加入bitset中,之後對於它的每個兒子結點,我們在bitset中查詢是否存在 對於已經存在的數取max,不存在的話加入bitset 同時加入當前結點的約數表,這樣保證我們每次查詢的時候都只需要往下查詢一層即可。
其實這個做法我後來想想還是能用dsu來優化一部分的,例如我們可以把重兒子的bitset不清空,那麼重兒子的bitset就可以加以利用,但是其實我的做法需要把兒子結點的約數表合併到父親結點,那麼還是要遍歷重兒子的約數表,那麼預先儲存的bitset感覺優化就不大了,但是其實也有解決的辦法,就是可以先把其他結點的約數合併到重兒子中,最後直接賦值給父親結點即可。不知道能優化多少,我也不想去寫了= =
Ac程式碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5; const int mod=1e9+7; const int INF=1e9+7; ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;} int n,tmp,ans[maxn]; vector<int> rv[maxn],val[maxn],v[maxn]; //rv儲存每個數約數 val儲存每個結點約數 v建圖 bitset<maxn> vs; void unite(int x,int y) //將y結點合併進x結點 { for(int i=0;i<val[y].size();i++) { if(vs.test(val[y][i])) tmp=max(tmp,val[y][i]); //如果bitset中已經存在取max else val[x].push_back(val[y][i]),vs.set(val[y][i]); //不存在則加入bitset以及x的約數表 } } void dfs(int x,int fa) { for(int i=0;i<v[x].size();i++) { int u=v[x][i]; if(u==fa) continue; dfs(u,x); } tmp=-1; for(int i=0;i<val[x].size();i++) vs.set(val[x][i]); //預先將當前結點約數加入bitset for(int i=0;i<v[x].size();i++) //將所有兒子結點的約數加入bitset { int u=v[x][i]; if(u==fa) continue; unite(x,u); } ans[x]=tmp; vs.reset(); //清空bitset } int main() { for(int i=1;i<maxn;i++) //打約數表 { for(int j=1;j<maxn;j++) { if(i*j>maxn) break; rv[i*j].push_back(i); } } scanf("%d",&n); for(int i=2;i<=n;i++) { int x; scanf("%d",&x); v[i].push_back(x); v[x].push_back(i); } for(int i=1;i<=n;i++) //得到每個結點的約數表 { int x; scanf("%d",&x); val[i]=rv[x]; } dfs(1,0); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); //system("pause"); }