定義任意兩點之間存在唯一路徑的無向圖是樹。對於一棵n個點的樹,如果刪掉某個點u之後每個連通塊的大小均不
超過n/2,那麼稱u為這棵樹的重心。現在有一棵n個點的樹t,利用過程p來構造乙個n個點的有向圖g,初始g沒有邊
。現在對t呼叫過程p,p的內容如下:
1:刪去u,對每個連通塊遞迴呼叫過程p;
2:對每個連通塊,如果它的標號最小的重心為v,那麼在圖g中連一條u到v的有向邊。
3:現在小q同學手裡有乙個圖g,但是不記得原來t的樣子了,希望你能通過g來恢復t,但是可能得到的t會有很多種
你只需要告訴小q同學可能的t的個數。
兩棵樹被認為是不同的,當且僅當存在一對點(u,v),使得u和v在一棵樹中有邊,在另一棵樹中沒有邊。
設f[i]表示以點分樹中i為根有多少種原樹。
對於i的兒子j,如果size[j]不是size[i]的一半,直接f[i]*=f[j]*size[j]。
否則,按照題目的定義,只能選取編號大的,我們找到j子樹中所有編號大於i的,設個數為k,則f[i]*=f[j]*k。
可以用線段樹合併,也可以暴力。暴力的複雜度是對的是因為顯然最壞情況是滿二叉樹,此時複雜度是n log n。
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10,maxtot=2000000+10,mo=1000000007;
ll f[maxn];
int tree[maxtot],left[maxtot],right[maxtot],root[maxn];
int size[maxn],h[maxn],go[maxn],next[maxn],d[maxn];
int i,j,k,l,t,n,m,tot,ca,ans,wdc;
void add(int
x,int
y)int newnode(int
x)void insert(int &x,int l,int r,int a)
int mid=(l+r)/2;
if (a<=mid) insert(left[x],l,mid,a);else insert(right[x],mid+1,r,a);
tree[x]=tree[left[x]]+tree[right[x]];
}int query(int
x,int l,int r,int a,int b)
int merge(int a,int b,int l,int r)
int mid=(l+r)/2;
left[a]=merge(left[a],left[b],l,mid);
right[a]=merge(right[a],right[b],mid+1,r);
tree[a]=tree[left[a]]+tree[right[a]];
return a;
}void dfs(int
x) t=h[x];
while (t)
insert(root[x],1,n,x);
}int main()
fo(i,1,n)
if (!d[i])
tot=0;
dfs(wdc);
ans=f[wdc];
(ans+=mo)%=mo;
printf("%d\n",ans);
}}
BZOJ 1012之線段樹解法
根據題意,很容易想到線段樹,單點修改,區間查詢,線段樹的模板大家都有,所以就不再做過多解釋。不過有一點值得一提,在我做這道題的時候使用了cin和cout,後來隊友告訴我,bzoj使用了o2優化,使用cin和cout可能會報錯,果然,我改了cin和cout之後就沒有問題了。include includ...
bzoj 3689 異或之 字典樹 堆
給定n個非負整數a 1 a 2 a n 對於每對 i,j 滿足1 i j n,得到乙個新的數a i xor a j 這樣共有n n 1 2個新的數。求這些數 不包含a i 中前k小的數。2 n 100000 1 k min 0 a i 2 31 先對所有數建一棵字典樹,對字典樹的每個節點記錄這個節點...
BZOJ 3689 異或之 Trie樹 堆
題目鏈結 description 給定n個非負整數a 1 a 2 a n 對於每對 i,j 滿足1 i j n,得到乙個新的數a i xor a j 這樣共有n n 1 2個新的數。求這些數 不包含a i 中前k小的數。注 xor對應於pascal中的 xor c 中的 input 第一行2個正整數...