Problem G. Depth-First Search
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 378 Accepted Submission(s): 73
Problem Description
Kazari is learning depth-first search. More precisely, she is doing an experiment about it.
Consider an unrooted tree with n vertices and an empty array called A.
She randomly chooses a vertex s as root and starts from s to walk around, following the rules below.
* When she enters a vertex x for the first time, she append x to A at once.
* If some adjacent vertex has not been visited, she randomly chooses one and walks into it.
* If all adjacent vertices have been visited,
* If she is at root, the experiment is done.
* If she is not at root, she walks into the vertex which is the most nearest to root.
Among all possible arrays that A is likely to be finally, Kazari wishes to count how many of them is lexicographically smaller than the given array B. Since the answer is too large, print it modulo 109+7.
Input
The first line of the input contains an integer T denoting the number of test cases.
Each test case starts with a positive integer n (∑n≤106), denoting the number of vertices.
The second line contains n integers B1,B2,...,Bn (1≤Bi≤n,∀i≠j,Bi≠Bj).
Each of next n−1 lines contains two integers u,v, representing an edge (u,v) on the tree.
Output
For each test case, print a non-negative integer denoting the answer modulo 109+7.
Sample Input
2 5 2 1 3 5 4 1 2 2 3 2 4 4 5 6 6 4 5 3 2 1 1 2 2 3 3 4 4 5 5 6
Sample Output
3 9
題意:一個n個節點的無根樹和長度爲n的序列A,要求隨意從一個點開始DFS,問有多少種DFS方法使得DFS序列的字典序小於序列A
題解:當前節點爲u,子節點個數爲sz[u]時,可以走的方案樹爲sz[u]!,易知選定一個根節點後,DFS的總方案數就爲,還可以知道每個點的子節點數是它的度數-1,根節點的子節點數就是它的度數。
首先考慮root,任何root編號小於A[0]的DFS方法一定是可行的。然後考慮root編號,此時需要走到root的一個子節點,易知其編號,然後可以分類討論下:
- 當編號<A[1]時,則接下來任意走都是字典序小於序列A的
- 當編號=A[1]時,繼續往下走,再與A[2]比較,依次類推
然後再計算一下方案數:設f[n] = n!,cnt[u][x]爲u的子節點中編號小於x的節點數,val[u]爲子樹u的DFS方案數,易知,其中v是子樹u中所有節點
爲了簡化計算,只考慮當前點是root的情況
- 先看root的子節點是否有編號爲A[1]的點
- 如果有,(v是u的子節點),然後遞歸到編號爲A[1]的點,回到第一步再找A[2]
- 如果沒有,(v是u的子節點),然後直接返回(因爲字典序已經小於序列A,下面子節點任意走即可)
解釋一下2中的式子(3的式子同2):
如果選擇先走任意一個編號小於A[1]的點,則之後一定可以任意走,相當於從cnt[u][A[1]]個點中選一個點放在第一個位置;然後剩下的sz[u] - 1個點就可以任意擺放,方案數爲(sz[u] - 1)!,因爲可以任意走,所以在子樹中行走的方案數爲,把這幾項乘起來即可。
#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int mod = 1e9 + 7;
const int MX = 1e6 + 5;
ll pow (ll a, int b) {
ll ret = 1;
while (b) {if (b & 1) ret = ret * a % mod; a = a * a % mod; b >>= 1;}
return ret;
}
inline ll mul (ll a, ll b, ll c) {return a * b % mod * c % mod;}
vector<int>E[MX];
inline void init (int n) {
rep (i, 1, n + 1) E[i].clear();
}
struct Tree {
int n;
vector <int> T;
void init (int _n) {
n = _n;
T.resize (n + 1);
for (int i = 0; i <= n; i++) T[i] = 0;
}
void add (int x, int v) {
for (int i = x; i <= n; i += i & -i) T[i] += v;
}
int sum (int x) {
if (x > n) x = n;
int ret = 0;
for (int i = x; i > 0; i -= i & -i) ret += T[i];
return ret;
}
} t[MX];
int n, a[MX];
ll f[MX], invf[MX], d, ans, index;
ll val[MX], inv[MX];
int solve (int u, ll v) {
index++; ll sum = 1;
rep(i, 0, E[u].size()) sum = sum * val[E[u][i]] % mod;
per(i, E[u].size(), 0) {
int nxt = lower_bound (E[u].begin(), E[u].end(), a[index + 1]) - E[u].begin();
int cnt = t[u].sum (nxt);
ans = (ans + mul (sum, f[i], v) * cnt) % mod;
if (nxt == E[u].size() || E[u][nxt] != a[index + 1]) return 1;
t[u].add (nxt + 1, -1);
sum = sum * inv[E[u][nxt]] % mod;
if (solve (E[u][nxt], mul (v, f[i], sum) ) ) return 1;
}
return 0;
}
void dfs (int u, int pre) {
if (pre > 0) E[u].erase(find(E[u].begin(), E[u].end(), pre));
val[u] = f[E[u].size()];
t[u].init (E[u].size());
rep(i, 0, E[u].size()) t[u].add (i + 1, 1);
if(!E[u].empty()) {
sort (E[u].begin(), E[u].end() );
for (auto v : E[u]) {
dfs (v, u);
val[u] = val[u] * val[v] % mod;
}
}
inv[u] = pow (val[u], mod - 2);
}
int main() {
//FIN;
f[0] = 1; rep (i, 1, MX) f[i] = f[i - 1] * i % mod;
invf[MX - 1] = pow (f[MX - 1], mod - 2);
per (i, MX - 1, 0) invf[i] = invf[i + 1] * (i + 1) % mod;
int T; cin >> T;
for(int cas = 1; cas <= T; cas++) {
scanf ("%d", &n);
init(n);
rep (i, 0, n) scanf ("%d", &a[i]);
rep (i, 1, n) {
int u, v;
scanf ("%d%d", &u, &v);
E[u].push_back (v);
E[v].push_back (u);
}
a[n] = 0, d = 1, ans = 0;
rep (i, 1, n + 1) d = d * f[E[i].size() - 1] % mod;
rep (i, 1, a[0]) ans = (ans + mul(d, f[E[i].size()], invf[E[i].size() - 1])) % mod;
index = -1;
dfs(a[0], -1);
solve (a[0], 1);
printf ("%lld\n", ans);
}
return 0;
}