CSP-S 模拟 我的订书机之恋(随机化乱搞)(巧妙建图)(LCA)

在这里插入图片描述
考虑一个 r r r 哪些 l l l 可以作为它的答案
就是不存在一个弦的左括号在区间内右括号在区间外或者左括号在区间外右括号在区间内
开始想的是打一个差分标记然后判断是不是为 0,发现凉了
因为存在这种情况:一个右括号在里面左括号在外面,左括号在里面右括号在外面
回到原来的问题,给定一个区间,问存不存在更这个区间交叉的区间
于是我们可以给每个区间分配一个 v a l val val,在 l , r l,r l,r 打上标记,然后求前缀异或和
显然两个都在里面会异或成 0,一个在里面会有 v a l val val 的贡献
于是一个合法的区间的异或和为 0,求一个前缀和,就是对 r 1 r_1 r1 统计前面为 r 1 r_1 r1 的个数
这里可以忽略掉 r 2 r_2 r2是因为 s u m r 2 ! = s u m r 1 sum_{r_2}!=sum_{r_1} sumr2!=sumr1 的话已经凉了,于是只用考虑 r 1 r_1 r1
离线用桶维护即可


正解:发现可以对每个点求出它作为右端点的极小答案区间
然后可以作为答案的区间就是这些极小区间并起来
有一个 显然 的策略是一个 r r r 跳到它的最小区间的左端点 l l l,然后再往左跳一步
如果跳到左括号已经凉了不能继续跳因为跳了一定跨出了一个区间
如果是右括号就可以用上述策略继续跳
于是有一个建图策略是 r r r l l l 的前一个连边,显然是一棵树
r 1 , r 2 r_1,r_2 r1,r2 l c a lca lca 的深度就是合法的左端点个数


#include<bits/stdc++.h>
#define cs const
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 2e6 + 5;
int n, m, l[N], r[N], ct;
bool isr[N];
typedef long long ll;
ll d[N], b[N]; int sz;
ll rnd(){ return (rand() | ((ll)rand() << 15)) * (rand() | ((ll)rand() << 15)); }
vector<int> v[N];
#define pb push_back
int ans[N], bin[N];
int main(){
	srand(time(0));
	freopen("hotchkiss.in","r",stdin);
	freopen("hotchkiss.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= (n << 1); i++){
		int x = read();
		if(x > i){ l[++ct] = i; r[ct] = x; isr[x] = true; } 
	}
	for(int i = 1; i <= ct; i++){
		d[l[i]] = d[r[i]] = rnd();
	}
	for(int i = 1; i <= (n << 1); i++){
		d[i] ^= d[i-1];
	}
	if(n <= 2e3){
		for(int i = 1; i <= m; i++){
			int r1 = read(), r2 = read();
			if(r1 > r2) swap(r1, r2);
			if(!r1){ puts("0"); continue; }
			if(!isr[r1] || !isr[r2]){ puts("0"); continue; }
			if(d[r1] != d[r2]){ puts("0"); continue; }
			int ans = 0;
			for(int l = 1; l < r1; l++) if(!isr[l] && d[l - 1] == d[r1]) ++ans;
			cout << ans << '\n';
		} 
	}
	else{
		for(int i = 1; i <= (n << 1); i++) b[++sz] = d[i]; b[++sz] = d[0];
		sort(b + 1, b + sz + 1); sz = unique(b + 1, b + sz + 1) - (b + 1);
		for(int i = 0; i <= (n << 1); i++) d[i] = lower_bound(b + 1, b + sz + 1, d[i]) - b;
		for(int i = 1; i <= m; i++){
			int r1 = read(), r2 = read();
			if(r1 > r2) swap(r1, r2);
			if(!r1){ ans[i] = 0; continue; }
			if(!isr[r1] || !isr[r2]){ ans[i] = 0; continue; }
			if(d[r1] != d[r2]){ ans[i] = 0; continue; }
			v[r1].pb(i);
		} 
		bin[d[0]]++;
		for(int i = 1; i <= (n << 1); i++){
			for(int j = 0; j < v[i].size(); j++){
				int id = v[i][j];
				ans[id] = bin[d[i]];
			} bin[d[i]]++;
		}
		for(int i = 1; i <= m; i++) cout << ans[i] << '\n';
	}
	return 0;
}
#include<bits/stdc++.h>
#define cs const
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 2e6 + 5;
int n, m, l[N], r[N], p[N];
int sta[N], top;
int fa[N], rt[N], f[N][20], dep[N];
vector<int> v[N];
void dfs(int u, int RT){
	rt[u] = RT;
	for(int i = 1; i <= 18; i++) f[u][i] = f[f[u][i-1]][i-1];
	for(int i = 0; i < v[u].size(); i++){ 
		int t = v[u][i]; 
		dep[t] = dep[u] + 1; f[t][0] = u; dfs(t, RT); 
	}
}
int lca(int x, int y){
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 18; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) x = f[x][i];
	if(x == y) return x;
	for(int i = 18; i >= 0; i--) if(f[x][i] ^ f[y][i]) x = f[x][i], y = f[y][i];
	return f[x][0];
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= (n << 1); i++) p[i] = read();
	for(int i = 1; i <= (n << 1); i++){
		l[i] = min(i, p[i]);
		r[i] = max(i, p[i]);
		while(top && l[i] <= sta[top]){
			int x = sta[top--];
			l[i] = min(l[i], l[x]);
			r[i] = max(r[i], r[x]);
		} sta[++top] = i;
	}
	memset(fa, -1, sizeof(fa));
	memset(rt, -1, sizeof(rt));
	for(int i = 1; i <= (n << 1); i++){
		if(r[i] == i){
			fa[i] = l[i] - 1;
			v[fa[i]].push_back(i); 
		}
	} 
	for(int i = 0; i <= (n << 1); i++){
		if(v[i].size() && !(~rt[i])) dep[i] = 1, dfs(i, i);
	}
	for(int i = 1; i <= m; i++){
		int r1 = read(), r2 = read();
		if(r1 > r2) swap(r1, r2);
		if(!r1){ puts("0"); continue; }
		if(!(~rt[r1]) || !(~rt[r2])){ puts("0"); continue; }
		if(rt[r1] ^ rt[r2]){ puts("0"); continue; }
		cout << dep[lca(r1, r2)] - 1 << '\n';
	} 
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值