【洛谷5002】ひたむきOI-先祖探し(樹上計数)
3419 ワード
OI-祖先を探す
テーマの背景
(Imakf)は小さなコンニャクで、最近習ったばかりで、携帯電話(APP)でゲームを見て(LCA)とダウンロードしました.
タイトルの説明
このゲームでは、(N)個のノードがあり、ルートノードが(R)、システムが(M)個の点(P_1,P_2...P_M)を選択し、(Imakf)に何組の点対((u_i,v_i))があるかを答える最近の共通の祖先が(P_i)です.(Imakf)こんにゃくなので、習ってもできないので、助けを求めるしかありません.
(Imakf)結局少し習ったことがあるので、彼はあなたに答えの型((10^9+7))を許可します.
にゅうしゅつりょくけいしき
入力形式:
最初の行(N,R,M).
その後(N-1)行ごとに2つの数(a,b)は(a,b)の間に1つのエッジを表します.
その後(1)行(M)個数表示(P_i)
出力フォーマット:
(M)行、行ごとに1つの数、ii行目の数は、点対((u_i,v_i))の最近の共通祖先がどれだけあるかを示す(P_i)
入出力サンプル
サンプル#1を入力:
7 1 3
1 2
1 3
2 4
2 5
3 6
3 7
1 2 4
出力サンプル#1:
31
7
1
説明
問い合わせ1:
(1,1)(1,2)(1,3)(1,4)(1,5)(1,6)(1,7)(2,1)(2,3)(2,7)(3,1)(3,2)(3,4)(3,5)(4,3)(4,3)(4,6)(4,7)(5,1)(5,6)(5,6)(5,6)(5,7)(5,6)(6,7)(6,2)(6,4)(6,4,5)(7,2)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,4)(7,5)(7,5)計31組
問い合わせ2:
(2,2)(2,4)(2,5)(4,2)(4,5)(5,2)(5,4)計7群
質問3:
(4,4)合計1組
データ範囲
\(N\leq10000,M\leq50000\)
問題解
これが洛谷の新しい問題だと見て、少し考えてやった.
私は(dfs)の時に(ans)配列を前処理しました.つまり、各点の答えなので、時間の複雑さは(max(n,m))です.
各ポイント(x)について、答えを(x)をまたぐシナリオと、(x)をまたぐシナリオの2つの部分に分けることができます.
xを越えていない部分:
明らかに、その中の1つの点は必ず(x)であるため、この部分の答えは(x)の(siz)サイズの(2)倍(-1)(点対であり(1,1)が1つであるため)である.
すなわち(ans 1=2*siz[x]-1)である.
(x)にまたがる部分:
乗算原理によれば、すべての(x)の息子(son_i)の(siz)サイズを乗算することがこの部分の答えである.
(x)全部で(k)本の木があるとすると[ans 2=sum_{i=1}^{k}sum_{j=1}^{k}siz[son[i]*siz[son[j]]\=sum_{i=1}^{k}siz[son[i]*(siz[x]-1)\=(siz[x]-1)*(siz[x]-1)]しかし、私たちは一部を重く計算したので、((i=j))の部分ではないことに気づきます.
だから私たちは(sum_{i=1}^{k}siz[son[i]^2)を減算します.
だから(ans 2=(siz[x]-1)^2-sum_{i=1}^{k}siz[son[i]]^2\)
総合:
\[ Ans=ans1+ans2\\=2*siz[x]-1+(siz[x]-1)^2-\sum_{i=1}^{k}siz[son[i]]^2\\=siz[x]^2-\sum_{i=1}^{k}siz[son[i]]^2\]
各サブツリーの(siz[son[i]^2)の和を維持するだけで答えを得ることができます.
ツッコミ一言:この问题Ans最大は(N^2)で、意外にも型を取りに行きます。。。
code:
#include
#include
#include
#include
#include
#define N 10005
#define R register
using namespace std;
templateinline void read(T &a){
char c=getchar();T x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
a=f*x;
}
int n,rt,m,tot,p;
int siz[N],ans[N],h[N],sum[N];
struct node{
int nex,to;
}edge[N<<1];
inline void add(R int u,R int v){
edge[++tot].nex=h[u];
edge[tot].to=v;
h[u]=tot;
}
inline void dfs(R int x,R int f){
siz[x]=1;
for(R int i=h[x];i;i=edge[i].nex){
R int xx=edge[i].to;
if(xx==f)continue;
dfs(xx,x);
siz[x]+=siz[xx];
sum[x]+=siz[xx]*siz[xx];
}
ans[x]=siz[x]*siz[x]-sum[x];
}
int main(){
read(n);read(rt);read(m);
for(R int i=1,u,v;i<=n-1;i++)
read(u),read(v),add(u,v),add(v,u);
dfs(rt,0);
while(m--){
read(p);
printf("%d
",ans[p]);
}
return 0;
}