[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),或者直接在开头一并清除.

posted @ 2019-02-06 22:18  Qrsikno  阅读(332)  评论(0编辑  收藏  举报