1. 程式人生 > >【汕頭市選2014】分叉(fork)

【汕頭市選2014】分叉(fork)

Description

給出一棵N 個點的樹,點的編號是1, 2,。。。,N。

對於3 個點{a,b,c},如果不存在一條簡單路徑同時經過a,b,c,那麼{a,b,c}是一個分叉。

統計不同分叉的數量。

樹 無環,連通的無向圖

簡單路徑 不重複經過同一個點的路徑

Input

第1 行,1 個整數N。接下來(N -1) 行,每行2 個整數Ai,Bi,表示點Ai 和點Bi 間有一條邊。

Output

1 個整數,表示所求的值。

Sample Input

5

1 2

1 3

1 4

1 5

Sample Output

4

Data Constraint

• 對於30% 的資料,N <= 100;

• 對於50% 的資料,N <= 1000;

• 對於100% 的資料,1 <= N <= 10^5。

思路

一開始,我想的是列舉兩個點,只要不是gcd,都滿足要求。很明顯,TLE。
由剛才的啟發,不如列舉gcd。
不難發現,若x為y和z的gcd,那麼他們一定在以x為根節點的兩個子樹當中。
so ans=C(n,3)-X。 X為不符合的個數。
X=sum*(n-sum-之前搜過的個數)

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; struct A { int x,y; }a[222222],b[222222]; long long sum[222222],fa[222222],son[222222]; long long n,k; bool f[222222]; long long ans=0; int cmp(A a,A b) { return a.x<b.x; } void star() { b[a[1].x].x=1; for(int i=2; i<=k; i++) { if
(a[i].x!=a[i-1].x) { b[a[i-1].x].y=i-1; b[a[i].x].x=i; } } b[a[k].x].y=k; } void dfs(int t) { f[t]=1; for(int i=b[t].x; i<=b[t].y; i++) { if(i!=0 && !f[a[i].y]) { fa[a[i].y]=t; son[t]++; dfs(a[i].y); sum[t]+=sum[a[i].y]; } } sum[t]++; } int main() { // freopen("fork.in","r",stdin); // freopen("fork.out","w",stdout); scanf("%d",&n); for(int i=1; i<=n-1; i++) { scanf("%d%d",&a[i].x,&a[i].y); a[i+n-1].x=a[i].y; a[i+n-1].y=a[i].x; } k=n*2-2; sort(a+1,a+k+1,cmp); //printf("done\n"); star(); dfs(1);//printf("done\n"); ans=n*(n-1)*(n-2)/6; //for(int i=1; i<=k; i++)printf("%d %d\n",sum[i],son[i]); for(int i=1; i<=n; i++) { if(son[i]>0) { int p=0; for(int j=b[i].x; j<=b[i].y; j++) if(a[j].y!=fa[i]) { ans-=sum[a[j].y]*(n-sum[a[j].y]-1-p); p+=sum[a[j].y]; } } } //printf("%lld",ans); cout<<ans; fclose(stdin); fclose(stdout); }