[CF1111E] Tree

Portal

题意很好懂, 就不讲了.

主要问题在Dp方程式, 设\(Dp[i][j]\)表示询问点中前\(i\)个点分成\(j\)个联通块的时候的方案数, 那么有:
\[ Dp[i][j] = Dp[i - 1][j - 1] + Dp[i - 1][j] * (j - h[i]) \]
\(h[i]\)表示一个点到根的链上有多少个询问点.

你发现询问点数很少. 并且是\(\sum k_i \leq balabalal\)的形式, 直接上虚树计算\(h[i]\)

时间复杂度\(O(nlogn + (\sum k) log (\sum k) + \sum m_i k _i)\)

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define Debug(s) debug("The massage in line %d, Function %s: %s\n", __LINE__, __FUNCTION__, s)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for(;!isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
    for(;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(LL x) {
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

const int Maxn = 200009, Mod = 1e9 + 7;
struct edge {
    int to, nxt;
}g[Maxn << 1];
int n, head[Maxn], e, q;
int fa[Maxn][21], dep[Maxn], Euler_clk, Beg[Maxn], End[Maxn];
void add(int u, int v) { g[++e] = (edge){v, head[u]}, head[u] = e; }
void dfsInit(int u, int pa) {
    fa[u][0] = pa, dep[u] = dep[pa] + 1;
    rep (i, 1, 20) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    Beg[u] = ++Euler_clk;
    for (int i = head[u]; ~i; i = g[i].nxt) {
        int v = g[i].to;
        if (v != pa) dfsInit(v, u);
    }
    End[u] = ++Euler_clk;
}

void init() {
    clar(head, -1);
    n = read(), q = read();
    rep (i, 1, n - 1) {
        int u = read(), v = read();
        add(u, v), add(v, u);
    }

    dfsInit(1, 0);
}

int LCA(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    drep (i, 20, 0) 
        if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
    if (u == v) return u;
    drep (i, 20, 0)
        if (fa[u][i] != fa[v][i]) 
            u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}

int dp[309], instack[Maxn];
vector <int> qset, Tmp;
stack <int> s;
vector <pair<int, int> > Fak;

struct Tree {
    edge g[Maxn << 1];
    int head[Maxn], e, h[Maxn], mark[Maxn], vised[Maxn];
    vector <int> ln;
    void init() {
        if (ln.size() == 0) clar(head, -1);
        rep (i, 0, ln.size() - 1) 
            head[ln[i]] = -1, h[ln[i]] = mark[ln[i]] = vised[ln[i]] = 0;
        ln.clear(), e = 0;
    }
    void add(int u, int v) {
        g[++e] = (edge){v, head[u]}, head[u] = e;
        if (!vised[u]) ln.push_back(u), vised[u] = 1;
    }
    void setMark(int u) { mark[u] = 1; }
    int getMark(int u) { return h[u] - mark[u]; }
    void travel(int u, int pa) {
        h[u] = h[pa] + mark[u];
        for (int i = head[u]; ~i; i = g[i].nxt) {
            int v = g[i].to;
            if (v != pa) travel(v, u); 
        }
    }
}lst;
int cmp(int u, int v) {
    return (u < 0 ? End[-u] : Beg[u]) < (v < 0 ? End[-v] : Beg[v]);
}

void solve() {
    rep (Fake, 1, q) {
        qset.clear(), Tmp.clear();
        int k = read(), m = read(), r = read();
        rep (i, 1, k) {
            int u = read();
            qset.push_back(u), instack[u] = 1;
            Tmp.push_back(u);
        }
        if (!instack[r]) qset.push_back(r), instack[r] = 1;
        sort(qset.begin(), qset.end(), cmp);    
        rep (i, 1, qset.size() - 1) {
            int l = LCA(qset[i - 1], qset[i]);
            if (!instack[l]) instack[l] = 1, qset.push_back(l);
        }
        if (!instack[1]) instack[1] = 1, qset.push_back(1);
        
        rep (i, 0, qset.size() - 1) qset.push_back(-qset[i]);
        sort(qset.begin(), qset.end(), cmp);    
        
        lst.init();
        rep (i, 1, k) lst.setMark(Tmp[i - 1]);
        
        while (!s.empty()) s.pop();
        rep (i, 0, qset.size() - 1) 
            if (qset[i] > 0) s.push(qset[i]);
            else {
                int u = s.top(); s.pop(); instack[u] = 0;
                if (u != 1) lst.add(s.top(), u), lst.add(u, s.top());
            }
        qset.clear(); 
        lst.travel(r, 0);

        int flag = 0;
        rep (i, 1, k) flag |= (lst.getMark(Tmp[i - 1]) >= m);
        if (flag) {
            puts("0");
            continue;
        }

        rep (j, 0, m) dp[j] = 0;
        rep (i, 1, k) {
            Fak.push_back(make_pair(lst.getMark(Tmp[i - 1]), Tmp[i - 1]));
//          printf("%d %d\n", lst.getMark(Tmp[i - 1]), Tmp[i - 1]);
        }
        sort(Fak.begin(), Fak.end());

        dp[0] = 1;
        rep (j, 1, k) 
            drep (l, min(j, m), 0) 
                if (l <= Fak[j - 1].first) dp[l] = 0;
                else {  
                    dp[l] = 1ll * dp[l] * (l - Fak[j - 1].first) % Mod;
                    if (l > 0) dp[l] = (1ll * dp[l] + dp[l - 1]) % Mod;
                }

        Tmp.clear(), Fak.clear();
        LL ans = 0;
        rep (i, 1, m) (ans += dp[i]) %= Mod;
        printf("%d\n", ans);
    }
}

int main() {
    freopen("CF1111E.in", "r", stdin);
    freopen("CF1111E.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}

真的蛋疼, 调了一下午的BUG

主要是判断答案不存在返回的时候要把东西一并清空(Line 98),或者直接在开头一并清除.

猜你喜欢

转载自www.cnblogs.com/qrsikno/p/10354182.html