当前位置: 首页 > news >正文

题解:AT_arc138_f [ARC138F] KD Tree

题意:平面上有 \(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;
}
http://www.gsyq.cn/news/18395.html

相关文章:

  • SP33 TRIP - Trip 个人题解
  • 经营不是老板一个人的事 - 智慧园区
  • Codeforces Round 1051 (Div. 2)[A ~E]
  • 【Azure APIM】解答REST API实现禁用自签名证书的证书链验证中的backends参数值从那里取值的问题?
  • 2025 AI 进化图谱:技术突破、场景落地与产业重构 - 指南
  • 题解:P14065 [PO Final 2022] 对弈 / Laserschack
  • CF2064E Mycraft Sand Sort
  • 20251010周五日记
  • HTML5拖放API核心功能解析
  • Umi-OCR_文字识别工具 免安装使用教程(附下载安装包)!永久免费,开源离线OCR识别软件下载
  • 表格识别:不仅能识别文字,更能理解表格的结构和逻辑关系,实现输出可编辑、可分析的结构化数据
  • docker容器的三大核心技术UnionFS(下) - 指南
  • P13274 [NOI2025] 三目运算符
  • B2002 Hello,World!【入门】
  • 华为链路聚合配置
  • iOS 26 软件性能测试全流程,启动渲染资源压力对比与优化策略 - 详解
  • 手机adb 调试自己
  • 2025 年公共/商场/学校/地铁/电影院/会所/机场/卫生间隔断厂家选购指南:优质厂商推荐与实用选择策略
  • Java环境安装备忘录
  • 详细介绍:标准型ELN成主流:定制型为何“遇冷”?
  • 【Linux】Ext系列文件系统(下) - 实践
  • 2025 年水产养殖降氨氮亚盐厂家最新推荐排行榜 ,助力北方对虾鱼塘螃蟹池塘养殖户轻松选购优质产品
  • 2025 年玻璃钢水箱生产厂家最新推荐榜单:含 30 吨 / 订做 / 消防 / 方形 / 拼装式 / 屋顶 / 大型产品,从产能与服务双维度精选优质企业
  • crontab 定时执行python脚本失败,但手动执行却成功问题处理 - hello-*
  • 2025 年不锈钢水箱厂家最新推荐榜:优质厂家实力对比与选购指南,助您选到适配设备矩形/屋顶/定做方形不锈钢水箱厂家推荐
  • 实用指南:Java 后端面试技术文档(参考)
  • 2025 年钢结构厂家最新推荐榜:优质企业全面解析,助力客户精准选择可靠合作伙伴
  • 2025规划馆运营厂家 TOP 榜:苏州金梓树智能科技,专注场馆全周期服务,规划馆运维优质服务商推荐!
  • 2025 高温线缆厂家 TOP 榜:奇温线缆 (上海) 有限公司,专注特种高温领域,定制化高温线缆源头厂家推荐!
  • OI 笑传 #17