HDU-6338 Problem G. Depth-First Search(計數)

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的總方案數就爲\prod sz[u],還可以知道每個點的子節點數是它的度數-1,根節點的子節點數就是它的度數。

首先考慮root,任何root編號小於A[0]的DFS方法一定是可行的。然後考慮root編號=A[0],此時需要走到root的一個子節點,易知其編號\leq A[1],然後可以分類討論下:

  1. 當編號<A[1]時,則接下來任意走都是字典序小於序列A的
  2. 當編號=A[1]時,繼續往下走,再與A[2]比較,依次類推

然後再計算一下方案數:設f[n] = n!,cnt[u][x]爲u的子節點中編號小於x的節點數,val[u]爲子樹u的DFS方案數,易知val[u]=\prod f[sz[v]],其中v是子樹u中所有節點

爲了簡化計算,只考慮當前點是root的情況

  1. 先看root的子節點是否有編號爲A[1]的點
  2. 如果有,ans = ans + cnt[u][A[1]] * f[sz[u]-1] * \prod val[v](v是u的子節點),然後遞歸到編號爲A[1]的點,回到第一步再找A[2]
  3. 如果沒有,ans=ans+sz[u]*f[sz[u]]*\prod val[v](v是u的子節點),然後直接返回(因爲字典序已經小於序列A,下面子節點任意走即可)

解釋一下2中的式子(3的式子同2):

如果選擇先走任意一個編號小於A[1]的點,則之後一定可以任意走,相當於從cnt[u][A[1]]個點中選一個點放在第一個位置;然後剩下的sz[u] - 1個點就可以任意擺放,方案數爲(sz[u] - 1)!,因爲可以任意走,所以在子樹中行走的方案數爲\prod val[v],把這幾項乘起來即可。

#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;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章