首页 > 其他分享 >[题解] P6773 [NOI2020] 命运

[题解] P6773 [NOI2020] 命运

时间:2023-11-10 09:45:14浏览次数:38  
标签:dep int 题解 sum sgt NOI2020 ls 端点 P6773

P6773 [NOI2020] 命运

给你一棵 \(n\) 个节点的树,要给每条边染成 \(0\) 或 \(1\)。
有 \(m\) 个限制 \((u, v)\) 满足 \(u\) 是 \(v\) 祖先,表示 \(u\) 到 \(v\) 的路径中至少有一条边被染成了 1。
求方案数。
\(n, m \le 5 \times 10^5\)。

首先会想到容斥,但是很没前途,所以考虑直接 dp。

一个自然的想法是设 \(f_{u, 0/1}\) 表示 限制的上端点在 \(u\) 的子树内限制全都满足,下端点在 \(u\) 的子树内但上端点不在的限制满足 / 不满足 的方案数。

但这样无法转移,死在对于 下端点在 \(u\) 的子树内但上端点不在的限制 的状态设计过于简略。

考虑到在下端点相同的情况下,如果上端点深度深的限制满足了,那么上端点浅的限制一定会被满足,同样如果上端点深的限制没被满足,上端点浅的也不会被满足。所以对于 下端点在 \(u\) 的子树内但上端点不在的限制 的满足情况,一定存在一个深度 \(dep\),满足深度小于 \(dep\) 的点全不满足,大于等于的全满足。所以可以用 最深的没被满足的限制的上端点 来刻画 下端点在 \(u\) 的子树内但上端点不在的限制 的满足情况。

所以设计状态为 \(f_{u, i}\) 表示 限制的上端点在 \(u\) 的子树内限制全都满足,下端点在 \(u\) 的子树内但上端点不在的限制 最深的没满足的上端点的深度为 \(i\) 的方案数。

转移考虑合并 \(u\) 和它的一个儿子 \(v\),分类讨论一下:

  • 边 \((u, v)\) 染成了 \(1\):这时下端点在 \(v\) 子树内的限制必然全部满足,转移是 \(f'_{u, i} \leftarrow \sum_{j = 0}^{dep_u} f_{u, i} \times f_{v, j}\)。
  • 边 \((u, v)\) 染成了 \(0\):这意味着 \(u\) 的 最深的没满足的限制的上端点的深度 和 \(v\) 的 最深的没满足的限制的上端点的深度 的最大值必须为 \(i\),转移是 \(f'_{u, i} \leftarrow \sum_{j = 0}^{i} f_{u, i} \times f_{v, j} + \sum_{j = 0}^{i - 1} f_{u, j} \times f_{v, i}\)。后面的式子的上表是 \(i - 1\) 是因为 \(j = i\) 时会算两遍。

合起来转移就是:

\[f'_{u, i} \leftarrow \sum_{j = 0}^{dep_u} f_{u, i} \times f_{v, j} + \sum_{j = 0}^i f_{u, i} \times f_{v, j} + \sum_{j = 0}^{i - 1} f_{u, j} \times f_{v, i} \]

这个看着就很前缀和,所以记 \(g_{u, i} = \sum_{j = 0}^i f_{u, i}\)。

然后转移式子就变成了:

\[f'_{u, i} \leftarrow f_{u, i} \times (g_{v, dep_u} + g_{v, i}) + g_{u, i - 1} \times f_{v, i} \]

这个直接转移是 \(O(n^2)\) 的,还要继续优化。

可以考虑拉到线段树上进行线段树合并。\(g_{v, dep_u}\) 可以先一次查询查出来,然后就是再线段树合并的过程中维护 \(sum_1 = g_{v, dep} + g_{v, i}\) 和 \(sum_2 = g_{u, i - 1}\),在叶子结点对应地乘起来就行。

时间复杂度 \(O(n \log n)\)。

实现还是比较简单的。

constexpr int MAXN = 5e5 + 9;
int n, m, dep[MAXN], rt[MAXN];
vector<int> G[MAXN], stk, up[MAXN];

struct Node {
	int ls, rs;
	int sum, mul;
	
	Node(): ls(0), rs(0), sum(0), mul(1) { return; }
	Node(int _ls, int _rs, int _su, int _mu):
		ls(_ls), rs(_rs), sum(_su), mul(_mu) { return; }
} sgt[MAXN * 20];

int New_Node() {
	static int tot = 0; int id = 0;
	if (stk.empty()) id = ++ tot;
	else id = stk.back(), stk.pop_back();
	sgt[id] = Node(); return id;
}
void Push_Tag(int p, int k) {
	cmul(sgt[p].sum, k), cmul(sgt[p].mul, k);
	return;
}
void Push_Down(int p) {
	if (sgt[p].mul ^ 1) {
		Push_Tag(sgt[p].ls, sgt[p].mul);
		Push_Tag(sgt[p].rs, sgt[p].mul);
		sgt[p].mul = 1;
	}
	return;
}
void Push_Up(int p) {
	sgt[p].sum = add(sgt[sgt[p].ls].sum, sgt[sgt[p].rs].sum);
	return;
}
void Update(int &p, int pos, int k, int L = 0, int R = n) {
	if (!p) p = New_Node();
	if (L == R) { sgt[p].sum += k; return; }
	Push_Down(p); int Mid = L + R >> 1;
	if (pos <= Mid) Update(sgt[p].ls, pos, k, L, Mid);
	else Update(sgt[p].rs, pos, k, Mid + 1, R);
	Push_Up(p); return;
}
int Query(int p, int l, int r, int L = 0, int R = n) {
	if (l <= L && R <= r) return sgt[p].sum;
	Push_Down(p); int Mid = L + R >> 1;
	if (r <= Mid) return Query(sgt[p].ls, l, r, L, Mid);
	if (Mid < l) return Query(sgt[p].rs, l, r, Mid + 1, R);
	return add(Query(sgt[p].ls, l, r, L, Mid), Query(sgt[p].rs, l, r, Mid + 1, R));
}
int Merge(int p, int q, int &sv, int &su, int L = 0, int R = n) {
	if (!p && !q) return 0;
	if (!p || !q) {
		if (!p) {
			cadd(sv, sgt[q].sum), Push_Tag(q, su);
			return q;
		} else {
			cadd(su, sgt[p].sum), Push_Tag(p, sv);
			return p;
		}
	}
	if (L == R) {
		int sumu = sgt[p].sum, sumv = sgt[q].sum;
		cadd(sv, sumv), cmul(sgt[p].sum, sv);
		cmul(sgt[q].sum, su), cadd(su, sumu);
		cadd(sgt[p].sum, sgt[q].sum); return p;
	}
	Push_Down(p), Push_Down(q);
	int Mid = L + R >> 1;
	sgt[p].ls = Merge(sgt[p].ls, sgt[q].ls, sv, su, L, Mid);
	sgt[p].rs = Merge(sgt[p].rs, sgt[q].rs, sv, su, Mid + 1, R);
	Push_Up(p); return p;
}

void dfs(int u, int fa) {
	dep[u] = dep[fa] + 1; int mxd = 0;
	for (auto v : up[u]) cmax(mxd, dep[v]);
	Update(rt[u], mxd, 1);
	for (auto v : G[u]) {
		if (v == fa) continue; dfs(v, u);
		int sv = Query(rt[v], 0, dep[u]), su = 0;
		rt[u] = Merge(rt[u], rt[v], sv, su);
	}
	return;
}

void slv() {
	n = Read<int>();
	for (int i = 1; i <= n - 1; i ++) {
		int u = Read<int>(), v = Read<int>();
		G[u].emplace_back(v), G[v].emplace_back(u);
	}
	m = Read<int>();
	for (int i = 1; i <= m; i ++) {
		int u = Read<int>(), v = Read<int>();
		up[v].emplace_back(u);
	}
	dfs(1, 0);
	Write(Query(rt[1], 0, 0), '\n');
	return;
}

标签:dep,int,题解,sum,sgt,NOI2020,ls,端点,P6773
From: https://www.cnblogs.com/definieren/p/17823402.html

相关文章

  • P9194 [USACO23OPEN] Triples of Cows P 题解
    Description给定一棵初始有\(n\)个点的树。在第\(i\)天,这棵树的第\(i\)个点会被删除,所有与点\(i\)直接相连的点之间都会两两连上一条边。你需要在每次删点发生前,求出满足\((a,b)\)之间有边,\((b,c)\)之间有边且\(a\not=c\)的有序三元组\((a,b,c)\)对数。\(n\leq2......
  • [题解] CF938G Shortest Path Queries
    ShortestPathQueries给你一张无向连通图,支持三种操作:插入一条边\((u,v,w)\)。删除一条边。求\((u,v)\)之间的异或最短路。\(n,m,1<2^{30}\)。先考虑异或最短路怎么求,这部分和最大XOR和路径是一样的。就是把图上的环都扔到一个线性基里,每次查询就是线性基......
  • [题解] P5901 [IOI2009] Regions
    P5901[IOI2009]Regions给你一棵树,每个点有颜色\(h_i\)。多次询问,每次询问有多少对\((u,v)\)满足\(u\)是\(v\)的祖先且\(u\)的颜色是\(r_1\)且\(v\)的颜色是\(r_2\)。\(n,q\le2\times10^5,h_i\le2.5\times10^4\)。总颜色数一定,考虑对颜色的出现次......
  • [题解] CFgym103069G Prof. Pang's sequence
    G.Prof.Pang'ssequence给一个长度为\(n\)的序列\(a\),多次询问区间\([l,r]\)内有多少个子区间的颜色数是奇数。\(n,m\le10^5\)。先按照HH的项链的套路,对于每个数记一下\(lst_i\)表示\(a_i\)上一次出现的位置。然后扫描线,将所有子区间的答案转化成历史和。......
  • [CSP-J 2021] 小熊的果篮 题解
    题目链接既然只有两种东西,我们不妨分开考虑,这里也借鉴了很多二分图题目的切入点。假设苹果和桔子下标分别如下图所示:苹果:1367910桔子:2458那么第一次取,应该是这样取:1234689也就是先取开头比较小的,然后轮流取,注意一定保证递增,也就是对于苹果找出来的\(x\)......
  • [题解]CFgym103470E Paimon Segment Tree
    PaimonSegmentTree区间加,求一段时间内的区间平方和。\(n,m,q\le5\times10^4\)。对时间维差分一下,变成询问区间历史平方和。离线下来扫描线,扫描线维护时间维,数据结构维护序列维。考虑维护二元组\((a,s)\)表示当前位置值为\(a\),历史平方和为\(s\)。可以发现怎......
  • A2OJ Ladder 21 简要题解
    https://earthshakira.github.io/a2oj-clientside/server/Ladder21.html只记录Difficultylevel>=8的。有很多题是口胡的。写了的会标注提交记录。还有些很久以前写过的题就懒得搬提交记录了。71.CF444EDZYlovesplanting我们二分答案,然后可以这样转化:把权\(\ged\)的......
  • 题解:疯狂lcm
    %你赛打到一半来写个题解link:疯狂lcm题意,求:\[\sum_{i=1}^{n}lcm(i,n)\]不多说废话,直接开推:\[\begin{aligned}&=n\sum_{i=1}^{n}\frac{i}{gcd(i,n)}\\&=n\sum_{d\midn}\sum_{i=1}^{n}\frac{i}{d}[gcd(i,n)=d]\\&=n\sum_{d\midn}\sum_{i=1}^{n}......
  • KubeEdge部署 完美运行 附问题解决方法
    云端部署环境准备一、部署前工作(k8s、docker安装及配置、初始化集群、golang安装、keadm安装)配置网络参数cat>>/etc/hosts<<EOF#GitHubStart52.74.223.119github.com192.30.253.119gist.github.com54.169.195.247api.github.com185.199.111.153assets-cdn.gith......
  • linux/docker 版 Sql Server新建的数据库插入中文乱码问题解决方案
    SqlServer插入遇到乱码原因:在英文系统中,SqlServer默认排序规则为英文字典顺序解决方案一:容器版SqlServer,在创建容器时,可以加上环境变量-eMSSQL_COLLATION=Chinese_PRC_CI_AS-eTZ=Asia/Shanghai 把排序规则设为中文字典顺序并忽略大小写区分重音,时区设置为上海,不然......