首页 > 其他分享 >【链交理论】CF1336F Journey

【链交理论】CF1336F Journey

时间:2024-01-26 16:22:47浏览次数:17  
标签:rt sz dep CF1336F int Journey lca dfn 链交

瞻仰遗迹,沐浴圣光。

Description

给出一颗 \(n\) 个节点的树,以及 \(m\) 条链,求有多少对链满足其交的边数 \(\geq k\)。

这个题其实有一个 Hint 是 CF1486F,比这个简单了很多倍。

Solution

我们考虑用 \((s, t, lca)\) 来表示一条 \(s \to t (dfn_s < dfn_t)\) 的链,其中 \(lca\) 表示 \((s, t)\) 的 LCA。

然后我们考虑两条链 \(c_1\) 和 \(c_2\)。

  • \(lca_1 \not = lca_2\) :

钦定 \(dep_{lca_1} < dep_{lca_2}\)。

这个我们发现肯定是类似大链上挂小链,考虑套路,我们考虑在小链上计算贡献,具体来说,我们可以钦定交的那一个端点在 \(c_2\) 上的一端,然后我们向那一端的向下走 \(k\) 步,其子树内的端点就必然可以交上 \(\geq k\) 条边。

  • \(lca_1 = lca_2\):

这个情况有很多个 case。

第一个 case 两个方向都有交。

这个比较好做,先钦定自己有一边是交完的那么我们枚举那个交完的,减去其贡献剩下的边数,我们只需要向另一端走 \(k - \Delta\) 步到 \(p\) 就好了,再统计交完的链中另一端在 \(p\) 的子树中就好了,这个可以自底向上来做,使用动态开点线段树对于相应 LCA 统计贡献就好了。

具体实现是枚举 \(s\),然后插入对应的 \(t\) 就好了。

第二个 case 是 \((s1, s2)\) 和 \((t1, t2)\) 交在一起。

这个我们可以考虑枚举 \((s1, s2)\) 的 LCA,一种想法是启发式合并虚树,另一种想法就是树上启发式合并。我们知道贡献是 \(x \to lca_1\) 这一段以及 \(lca_1 \to LCA(t_1, t_2)\) 这一段。我们可以启发式合并的时候按照刚才的想法,向 \(t_1\) 走 \(k - \Delta\) 步,再统计在其子树内的对应端点数。仍然还是动态开点线段树。

第三个 case 是 \((s_1, t_1)\) 和 \((s_2, t_2)\) 并在了一段。

这个比较简单,只需要插入 \(s\) 这个端点,查询向 \(t\) 走 \(k\) 步的子树内端点个数就好了,他还是动态开点线段树。

复杂度大概是 \(O(n\log^2 n)\)。

Implement

  • 注意到动态开点线段树只用开一颗,因为各个 case 都互不影响。
  • 有一点点细节,还是比较清新的。
// LUOGU_RID: 144464014
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; i ++)
#define per(i, r, l) for (int i = r; i >= l; i --)

using namespace std;

const int _ = 1.5e5 + 5;

int read() {
	int X = 0;
	char ch = 0, w = 0;
	while (ch < 48 || ch > 57)ch = getchar(), w |= (ch == '-');
	while (ch >= 48 && ch <= 57)X = X * 10 + (ch ^ 48), ch = getchar();
	return w ? -X : X;
}

int n, m, k, dfc;
int pa[_][20], dep[_], dfn[_], tr[_], sz[_], son[_], val[_];
vector <int> e[_];
long long ans;

void dfs1 (int x, int fa) {
	dep[x] = dep[fa] + 1, sz[x] = 1, pa[x][0] = fa;
	dfn[x] = ++ dfc, tr[dfc] = x;
	rep(i, 1, 19) pa[x][i] = pa[pa[x][i - 1]][i - 1];
	for (int y : e[x]) 
		if (y ^ fa) {
			dfs1(y, x);
			sz[x] += sz[y];
			if (sz[son[x]] < sz[y]) son[x] = y; 
 		}
}
int LCA (int x, int y) {
	if (dep[x] < dep[y]) swap(x, y);
	int d = dep[x] - dep[y];
	rep(i, 0, 19) if (d & (1 << i)) x = pa[x][i];
	if (x == y) return x;
	per(i, 19, 0) if (pa[x][i] ^ pa[y][i]) x = pa[x][i], y = pa[y][i];
	return pa[x][0]; 
} 
int kthanc (int x, int k) {
	rep(i, 0, 19) if (k & (1 << i)) x = pa[x][i];
	return x;
}

struct chain { int s, t, lca; } ;
vector <chain> vs[_], vm[_];

int rt[_];
template <int S>
struct SegMentTree {
	int tot, lc[S], rc[S], sz[S];
	SegMentTree() { 
		tot = 0, memset(sz, 0, sizeof(sz));
		memset(lc, 0, sizeof(lc)), memset(rc, 0, sizeof(rc));
	}
	void insert (int &x, int l, int r, int v, int k) {
		if(!x) x = ++ tot; sz[x] += k;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		v <= mid ? insert(lc[x], l, mid, v, k) : insert(rc[x], mid + 1, r, v, k);
	}
	int query (int x, int l, int r, int ql, int qr) {
		if (!x) return 0;
		if (ql <= l && r <= qr) return sz[x];
		int mid = (l + r) >> 1, res = 0;
		if (ql <= mid) res += query(lc[x], l, mid, ql, qr);
		if (qr > mid) res += query(rc[x], mid + 1, r, ql, qr);
		return res;
	}
	int merge (int x, int y) {
		if (!x || !y) return x | y;
		sz[x] += sz[y];
		lc[x] = merge(lc[x], lc[y]), rc[x] = merge(rc[x], rc[y]);
		return x;	
	}
} ;
SegMentTree <_ * 256> T;

void dfs3 (int x, int fa) {
	for (int y : e[x]) if (y ^ fa) dfs3(y, x), rt[x] = T.merge(rt[x], rt[y]);
	T.insert(rt[x], 1, n, dfn[x], val[x]);
	for (auto [u, v, lca] : vm[x]) 
		T.insert(rt[x], 1, n, dfn[u], -1), T.insert(rt[x], 1, n, dfn[v], -1);
	for (auto [u, v, lca] : vm[x]) {
		if (dep[u] - dep[x] >= k) {
			u = kthanc(u, dep[u] - dep[x] - k);
			ans += T.query(rt[x], 1, n, dfn[u], dfn[u] + sz[u] - 1);
		} 
		if (dep[v] - dep[x] >= k) {
			v = kthanc(v, dep[v] - dep[x] - k);
			ans += T.query(rt[x], 1, n, dfn[v], dfn[v] + sz[v] - 1);
		}
	}
}
void dfs4 (int x, int fa, int t) {
	for (int y : e[x]) if (y ^ fa && y ^ son[x]) dfs4(y, x, 0);
	if (son[x]) dfs4(son[x], x, 1);
	for (auto [u, v, lca] : vs[x]) {
		int p = v;
		if (dep[u] + dep[v] - 2 * dep[lca] >= k) {
			v = kthanc(v, dep[v] - dep[lca] - max(0, k - dep[u] + dep[lca]));
			ans += T.query(rt[lca], 1, n, dfn[v], dfn[v] + sz[v] - 1);
		}
		T.insert(rt[lca], 1, n, dfn[p], 1);
	}
	for (int y : e[x]) {
		if (y == fa || y == son[x]) continue ;
		rep(id, dfn[y], dfn[y] + sz[y] - 1)
			for (auto [u, v, lca] : vs[tr[id]]) {
				if (dep[lca] > dep[x] || dep[x] + dep[v] - 2 * dep[lca] < k) continue ;
				v = kthanc(v, dep[v] - dep[lca] - max(0, k - dep[x] + dep[lca]));
				ans += T.query(rt[lca], 1, n, dfn[v], dfn[v] + sz[v] - 1);
			}
		rep(id, dfn[y], dfn[y] + sz[y] - 1) {
			for (auto [u, v, lca] : vs[tr[id]]) {
				T.insert(rt[lca], 1, n, dfn[v], 1);
			}
		}
	}
	if (!t) {
		rep(id, dfn[x], dfn[x] + sz[x] - 1)
			for (auto [u, v, lca] : vs[tr[id]]) {
				T.insert(rt[lca], 1, n, dfn[v], -1);
			}
	}
}
int main () {
	n = read(), m = read(), k = read();
	rep(i, 1, n - 1) {
		int x = read(), y = read();
		e[x].push_back(y), e[y].push_back(x);
	}
	dfs1(1, 0);
	rep(i, 1, m) {
		int x = read(), y = read(), lca;
		lca = LCA(x, y), val[x] ++, val[y] ++;
		if (dfn[x] > dfn[y]) swap(x, y);
		vs[x].push_back({x, y, lca}), vm[lca].push_back({x, y, lca});
	}
	dfs3(1, 0);
	rep(i, 1, n) rt[i] = 0;
	dfs4(1, 0, 0);
	rep(i, 1, n) {
		for (auto [x, y, z] : vm[i]) T.insert(rt[i], 1, n, dfn[x], 1);
		for (auto [x, y, z] : vm[i]) {
			if (dep[y] - dep[i] >= k) {
				y = kthanc(y, dep[y] - dep[i] - k);
				ans += T.query(rt[i], 1, n, dfn[y], dfn[y] + sz[y] - 1);
			}
		}
	}
	cout << ans << endl;
	return 0;
}
/*
13 8 3
7 6
9 11
5 6
11 3
9 7
2 12
4 3
1 2
5 8
6 13
5 10
3 1
10 4
10 11
8 11
4 9
2 5
3 5
7 3
8 10
*/

标签:rt,sz,dep,CF1336F,int,Journey,lca,dfn,链交
From: https://www.cnblogs.com/Custlo/p/17989638

相关文章

  • Midjourney模拟API生图调用
    目前Midjourney没有对外开放API接口,所以通过MJ自动化生图的主要方式是,集成Discord应用机器人,通过机器人与MJ机器人进行交互,并监听频道内的生图结果,最终拿到图片地址。简单介绍下步骤一、购买MJ账号二、获取账号Authorization在网页中向MidjourneyBot发送/imagine进行生图我......
  • Midjourney获取seed值
    一、请求:envelope:curl'https://discord.com/api/v9/channels/1116337317276287057/messages/1124159098066305135/reactions/%E2%9C%89%EF%B8%8F/%40me?location=Message&type=0'\-X'PUT'\-H'authority:discord.com'\-H'......
  • Midjourney人物一致性探索
    原图a25yearsoldmaleinancientChineserobewithblackbigbackhairandsmalleyesandnormalnoseandsmallmouthandasmallface垫图https://s.mj.run/gBMdKDa7UJo,a25yearsoldmaleinancientChineserobewithblackbigbackhairandsmalle......
  • 从新手到高手:AI绘画实战中的Midjourney
    ......
  • Midjourney模拟API生图调用
    目前Midjourney没有对外开放API接口,所以通过MJ自动化生图的主要方式是,集成Discord应用机器人,通过机器人与MJ机器人进行交互,并监听频道内的生图结果,最终拿到图片地址。简单介绍下步骤一、购买MJ账号二、获取账号Authorization在网页中向MidjourneyBot发送/imagine进行生图我......
  • Midjourney获取seed值
    一、请求:envelope:curl'https://discord.com/api/v9/channels/1116337317276287057/messages/1124159098066305135/reactions/%E2%9C%89%EF%B8%8F/%40me?location=Message&type=0'\-X'PUT'\-H'authority:discord.com'\-H'......
  • Midjourney人物一致性探索
    原图a25yearsoldmaleinancientChineserobewithblackbigbackhairandsmalleyesandnormalnoseandsmallmouthandasmallface垫图https://s.mj.run/gBMdKDa7UJo,a25yearsoldmaleinancientChineserobewithblackbigbackhairandsmalle......
  • Journey to Salesforce:帮助10万人开启Salesforce职业生涯!
    JourneytoSalesforce计划始于2019年,是一个综合性计划,旨在帮助有抱负的Salesforce开发人员提高技能,快速走上职业道路,并在Salesforce生态系统中顺利就业。到目前为止,JourneytoSalesforce已成功帮助10万多名学习者开启Salesforce开发人员的职业生涯。JourneytoSalesforce正在......
  • Meta、Midjourney、DALL-E 3、 Adob​​e Firefly 绘图对比
    Meta刚刚推出了新的人工智能图像生成器。但它有多好呢?我将其与Midjourney、DALL-E3和AdobeFirefly的10个图像类别进行了比较。结果如下:写实照片提示:一位戴着蓝色围巾的快乐年轻女子在圣托里尼度假、白色建筑和蓝色圆顶的特写镜头2.风景照片提示:无人机航拍波拉波拉岛令人惊......
  • (Lora训练)(承接midjourney数据修改)(建对应名称txt与删txt内部后缀,括号,数字与转换下划线)Lo
    importosimportredefcreate_txt_from_image():#请求用户输入文件夹地址root_folder=input("请输入图片所在文件夹的完整路径:")#判断路径是否存在ifnotos.path.exists(root_folder):print("路径不存在,请检查输入的地址。")return#用......