尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

题解:AT_arc138_f [ARC138F] KD Tree

题解:AT_arc138_f [ARC138F] KD Tree
📅 发布时间:2026/6/27 11:36:17

题意:平面上有 \(n\) 个点 \((i,p_i)\),\(p\) 是一个排列。每次操作可以选择 \(x/y\) 和一个坐标,将点列分成左右/上下两边(保持两边的相对顺序不变),分别递归下去,直到只剩下一个点,把它加入答案序列末尾。求最终能生成多少种不同的答案序列。

做法:

首先很自然地考虑目前剩下的矩形为 \(x\in[l,r],y\in[u,d]\),记此时的答案为 \(f_{l,r,u, d}\)。考虑一刀劈成两半继续递归,但是显然这样直接算是错的,考虑如下情况:

  • 只有 \((1,1),(2,2)\) 两个点。

此时你做对 \(x\) 轴和 \(y\) 轴的操作是没有区别的。

那么对于这种会重复的我们一般考虑加一些限制使他不会重复。这里我们先定义一些东西,考虑现在这个区域中剩下了 \(k+1\) 个点,离散后应该会剩下 \(x_1<x_2<\cdots <x_k\),\(y_1<y2<\cdots <y_k\) 这些 切割线是有用的。我们考虑这么定义一个操作的字典序,按字典序从小到大排是 \(x_1,y_1,x_2,y_2,\cdots,x_k,y_k\)。这样排有什么好处呢?按字典序我们就可以让一种答案序列对应唯一一种操作序列,同时交错排布会有利于我们之后的分讨。

考虑如果一个操作序列第一刀切在的 \(x_i\) 这个位置,我们先算出来切在这里的总方案数,为 \(f_{l,x_i,u,d}\times f_{x_i+1, r,u,d}\)。但是有可能有操作序列字典序更小的得到了这个答案序列,考虑是 \(x_j\) 还是 \(y_j\)。

  • 由 \(x_j(j<i)\) 得到。

画图,那么应该分为这么几块:

那么形如 ABC 这样的操作序列就会被 \(x_j\) 解决而不是 \(x_i\),三部分方案乘起来即可。

  • 由 \(y_j (j<i)\) 得到。

这样会分成四块:

考虑 \(x_i,y_j\) 分别切出来的顺序会是怎样,\(x_i\) 会是 (AB)(DC),\(y_j\) 会是 (AD)(BC)。考虑什么时候会重复计算,发现只能是 \(D\) 为空的时候我们才会记重,所以就必须得满足 \(D\) 为空才会减去。

\(y\) 的同样讨论,读者可以自己推一下。

到这里已经可以写了,只不过写起来需要分讨很多。我们这里其实会发现一个事情,就是假设我们 \(x\in[l,r],y\in[u,d]\) 的矩形包含了集合为 \(S\) 的点,那么令 \(Tx_i\) 代表 \(x_i\) 下方切出来的部分,\(Ty\) 类似定义,那么我们考虑按照字典序扫描,那么只有前面的是后面的子集的时候才会被重复统计,其实感觉也是比较显然的。那么这样就可以比较方便地实现了。因为这些集合都是矩形交出来的,所以其实只有 \(O(n^4)\) 个,不用担心状态过多。

总复杂度 \(O(n^6)\)。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 35, mod = 1e9 + 7;
int n, p[maxn], q[maxn];
map<int, int> mp;
int popcnt(int x) {int cnt = 0;while(x)cnt += x % 2, x >>= 1;return cnt;
}
int cnt = 0;
int cal(int s) {if(popcnt(s) <= 1)return mp[s] = 1;if(mp.count(s))return mp[s];
//	cnt++;
//	if(cnt <= 10)
//		cout << s << endl;vector<int> x, y;for (int i = 1; i <= n; i++) {if((s >> i - 1) & 1)x.push_back(i), y.push_back(p[i]);} sort(y.begin(), y.end());vector<int> t;int nw1 = 0, nw2 = 0;for (int i = 0; i < x.size() - 1; i++) {nw1 |= (1 << x[i] - 1);t.push_back(nw1);nw2 |= (1 << q[y[i]] - 1);t.push_back(nw2);//	if(cnt <= 5);//		cout << s << " " << nw1 << " " << nw2 << endl;}vector<int> dp; dp.resize(t.size());int ans = 0;for (int i = 0; i < t.size(); i++) {dp[i] = cal(t[i]);for (int j = 0; j < i; j++)if((t[i] & t[j]) == t[j])dp[i] = (dp[i] - dp[j] * cal(t[i] ^ t[j]) % mod + mod) % mod;ans += dp[i] * cal(s ^ t[i]) % mod; ans %= mod;}
//	cout << s << " " << ans << endl;return mp[s] = ans;
}
signed main() {cin >> n;for (int i = 1; i <= n; i++)cin >> p[i], q[p[i]] = i;cout << cal((1 << n) - 1) << endl;return 0;
}

相关新闻

  • 如何在 Spring Boot 应用中配置多个 Spring AI 的 LLM 客户端
  • [Git] 放弃暂存区的修改
  • 前端里面transform和transition 属性的区别

最新新闻

  • Type-C一拖多快充线:智能功率分配与选购指南
  • 94个公共Tracker服务器:彻底终结BT下载卡在99%的终极解决方案
  • 生产环境下的Agent记忆机制设计:短期上下文与长期向量库的工程化取舍
  • 硬件预取器安全挑战与PhantomFetch防御技术解析
  • 基于4G和GPS的智慧养殖物联网终端设计与优化
  • 前端XSS攻击防御实战:从原理到2025年立体化安全方案

日新闻

周新闻

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号