qoj 3086 Edge Subsets

news/2025/2/24 22:23:52/文章来源:https://www.cnblogs.com/rizynvu/p/18735104

截至目前该题的题解都给出了一个比较厉害的转化方式,但这篇题解介绍的是如何不通过厉害的观察做出这题。

先考虑拆分一下问题使得问题具有某些特殊性。
首先因为连边的编号差都为 \(A\)\(B\),所以对于 \(i \not \equiv j\pmod {\gcd(A, B)}\),那么 \(i, j\) 的选取一定是不会互相影响的。
于是首先可以考虑按照 \(i\bmod \gcd(A, B)\) 分组,不同的组互相不干扰,方案数就可以直接相乘。

分组后的好处是处理每一组的 \(A, B\) 实际上是 \(\frac{A}{\gcd(A, B)}, \frac{B}{\gcd(A, B)}\)
也就是说此时 \(\gcd(A, B) = 1\),每一组的每一个点一定都是有用的。

接下来考虑如何处理这个问题。

首先有一个非常暴力的做法。
考虑每一个匹配都由右端点来决定,那么对于 \(i\) 来说实际上只关心 \(i - A\)\(i - B\) 能否匹配上。
那么对于 \(j < i - B\) 来说一定就不在 \(i\) 的考虑范围内,因为不管是 \(i\) 还是 \(i' > i\)\(j\) 都是没用的。
于是只需要考虑 \([i - B, i)\)\(B\) 个位置是否已经被匹配上,每次加入 \(i\) 时尝试与 \(i - A, i - B\) 配对并更新。
直接把 \(B\) 个位置的信息状压 dp 就可以做到 \(\mathcal{O}(n2^{B})\)

上面一个做法的坏处是在 \(B\) 太大的时候复杂度太劣。
于是这启发去尝试一些 \(B\) 比较大的时候的做法。
因为这题看着维护的信息就比较多,所以这部分的做法可能依然是个状压。
于是就要尝试让状压的幂次带有 \(\frac{1}{B}\) 状物。

于是可以尝试 \(\frac{n}{B}\),考虑这个的实际意义是在 \(n\) 个位置中按 \(B\) 个分段,段数就为 \(\frac{n}{B}\)
进一步的,因为关心的是位置,可以考虑把这个 \(\frac{n}{B}\) 当作从每一段中选一个位置,选出的的位置数是 \(\frac{n}{B}\) 的。
再进一步,按 \(B\) 个分段给出了一个很好的性质:如果只考虑编号差为 \(B\) 的边,那么有可能有连边的点编号一定 \(\bmod B\) 相同。
结合上面的想法,就可以把每一段中编号 \(\bmod B\) 的点放在一起状压。

因为此时状态里的点就是编号差为 \(B\) 的点,于是对于编号差为 \(B\) 的连边就很好做了。
具体来说,可以直接考虑状态里 \(i\)\(i + B\) 的点要不要匹配上并更新状态。
需要注意的是,可能会在过程中因为多种顺序而记重,解决的方式是类似高位前缀和,外层枚举 \(i\) 内层枚举状态转移。

接下来就考虑一个新的问题:如何转移编号差为 \(A\) 的边。
首先因为状态中的点 \(\bmod B\) 都相等,设为 \(i\)
那么接下来转移编号差为 \(A\) 的边,就可以考虑直接从 \(\bmod B = i\) 跳到 \(= (A + i) \bmod B\) 的点集,此时因为 \(\gcd(A, B) = 1\),所以保证了每条编号差为 \(A\) 的边都能够被跳到。

接下来考虑对于状态 \(f_s\) 记录的应该是点集里的点有无被匹配上的方案数。
那么考虑这个 \(s\) 中的点 \(x\bmod B = i\) 要满足什么才能如何匹配上 \(x + A\)

  • \(x\)\(x + A\) 有连边。
  • \(x\) 应该在 \(s\) 中属于为匹配的一类。

于是首先对于 \(s\) 中已经匹配上的点以及不存在与后面 \(+A\) 的连边的点肯定无法产生匹配。
那么剩下的点就是可以与后面的点产生匹配的点,把点集记为 \(t\)
那么对于一个点集 \(p\subseteq t\),就可以把 \(p\) 中的点与后面的点匹配,那么对于 \((i + A)\bmod B\) 的状态就是 \(p\) 中的点已经匹配上了,而不在 \(p\) 中的点都没有匹配。
于是所有 \(p\subseteq t\) 都可以得到 \(f_s\) 的方案数,那么这可以写成一个高位后缀和的形式,用 FMT-and 即可。

但是此时还有一点问题,就是 \(-A\bmod B\)\(0\) 之间的匹配问题。
因为每次 dp 都是考虑的从 \(i\) 跳到 \((i + A)\bmod B\),但是这个跳的是一个环,对于最后跳的环边有可能并不满足起始状态的限制。
对此可以直接暴力枚举这条换边的情况,也就是暴力枚举 \(-A\bmod B\)\(0\) 直接匹配的情况,如果合法再得到初始状态 dp。

于是就可以在 \(\mathcal{O}(B\times \frac{n}{B}\times 2^{2\frac{n}{B}}) = \mathcal{O}(n2^{2\frac{n}{B}})\) 的复杂度解决 \(B\) 较大的时候。

根据 \(B\) 的大小选择做法,平衡两个做法的复杂度,可以做到 \(\mathcal{O}(n2^{\sqrt{2n}})\)

实际写的时候可以直接当 \(B\le 20\) 用做法 1 否则用做法 2。

#include<bits/stdc++.h>
using ll = long long;
constexpr ll mod = 998244353;
inline void add(ll &x, ll y) { x = (x + y) % mod; }
constexpr int maxn = 6e2 + 10;
inline ll calc(int n, int A, int B, const auto &G) {if (B <= 20) {static ll f[1 << 20], g[1 << 20];memset(f, 0, sizeof(f)), f[0] = 1ll;const int mask = (1 << B) - 1;for (int i = 0; i < n; i++) {memcpy(g, f, sizeof(g)), memset(f, 0, sizeof(f));for (int s = 0; s <= mask; s++) {add(f[(s << 1) & mask], g[s]);if (i >= A && G[i - A][i] && (~ s >> A - 1 & 1)) {add(f[(s << 1 | 1 << A | 1) & mask], g[s]);}if (i >= B && G[i - B][i] && (~ s >> B - 1 & 1)) {add(f[(s << 1 | 1 << B | 1) & mask], g[s]);}}}ll ans = 0ll;for (int s = 0; s <= mask; s++) add(ans, f[s]);return ans;} else {static ll f[1 << 10], g[1 << 10];n = ((n - 1) / B + 1) * B;const int D = n / B;const int mask = (1 << D) - 1;ll ans = 0ll;for (int U = 0; U <= mask; U += 2) {bool chk = true;for (int i = 0; i < D; i++) {if (U >> i & 1) {chk &= G[i * B - A][i * B];}}if (chk == false) continue;memset(f, 0, sizeof(f)), f[U] = 1;for (int w = 0; ; w = (w + A) % B) {for (int i = 1; i < D; i++) {for (int s = 0; s <= mask; s++) {if ((~ s >> i & 1) && (~ s >> i - 1 & 1) && G[(i - 1) * B + w][i * B + w]) {add(f[s | 1 << i | 1 << i - 1], f[s]);}}}int lw = w + A >= B, H = 0;for (int i = 0; i < D; i++) {if (G[i * B + w][i * B + w + A]) {H |= 1 << i + lw;}}memset(g, 0, sizeof(g));for (int s = 0; s <= mask; s++) {add(g[((mask ^ s) << lw) & H], f[s]);}for (int i = 0; i < D; i++) {if (~ H >> i & 1) continue;for (int s = 0; s <= mask; s++) {if (s >> i & 1) {add(g[s ^ (1 << i)], g[s]);}}}memcpy(f, g, sizeof(f));if (w == B - A) break;}ans = (ans + f[U]) % mod;}return ans;}assert(false);
}
int N[maxn], G[maxn][maxn][maxn];
int main() {int n, m, A, B;scanf("%d%d%d%d", &n, &m, &A, &B);int g = std::__gcd(A, B);A /= g, B /= g;for (int i = 0; i < n; i++) N[i % g]++;for (int i = 1, x, y; i <= m; i++) {scanf("%d%d", &x, &y), x--, y--;int id = x % g; assert(x % g == y % g);G[id][x / g][y / g] = G[id][y / g][x / g] = 1;}ll ans = 1ll;for (int i = 0; i < g; i++) {ans = ans * calc(N[i], A, B, G[i]) % mod;}printf("%lld\n", ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/889231.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【APP逆向35】frida反调试2

前言:有些app运行时会监测frida的相关特征,监测到之后就会直接闪退 示例:解决方法:可以尝试使用strongR-frida-android来绕过监测1.下载frida-server(加强版)下载地址:https://github.com/hzzheyang/strongR-frida-android/releases?page=4 找到frida对应的版本解压,上…

【作业】自我介绍

软件工程学第一节课作业:自我介绍+软工5问作业相关信息这个作业属于哪个课程🔗班级链接 这个作业要求在哪里🔗作业链接 这个作业的目标 1. 自我介绍 2. 软工五问作业一: 自我介绍 🚩Hi! I am Mike, a technology learner enthusiastic in new ideas and programs. Toge…

磁盘总结---特殊符号系列-正则概述

1.系统管理-补充 文件系统: 磁盘中文件的组织方式常见文件系统 说明xfs centos 7默认的文件系统即可ext4 centos 6.x ubuntu 默认的文件系统ext3 centos 5.x 默认的文件系统swap 交换分区,也算是个文件系统2.磁盘性能指标磁盘性能指标 说明吞吐量(读写速度) 一般值得是磁盘读…

多态的前提--java进阶day02

1.多态的前提条件第一点和第二点都很好理解,第三点父类引用指向子类对象是什么意思?以下图进行讲解我们以前的写法,如下图,叫做子类引用指向子类那父类引用呢?就是把左边换成父类Animal即可因为dog和cat都是Animal的子类,所以二者皆可用该种引用方式并不是所有的对象都可…

行内块应用

这是做的作业<!DOCTYPE html> <html lang="zh-cn"> <head><meta charset="UTF-8"><title>Title</title><style>.pics {width: 1850px;height: 700px;display: flex;justify-content: center; /* 水平居中 */ali…

基于NURBS曲线的数据拟合算法matlab仿真

1.程序功能描述 基于NURBS曲线的数据拟合算法,非均匀有理B样条(Non-Uniform Rational B-Splines,简称NURBS)曲线是一种强大的数学工具,广泛应用于计算机图形学、CAD/CAM系统、几何建模和数据拟合等领域。NURBS曲线通过控制顶点和权重,能够精确地表示复杂的曲线和曲面…

基于simulink的PEM燃料电池控制系统建模与仿真,对比PID,积分分离以及滑模控制器

1.课题概述 基于simulink的PEM燃料电池控制系统建模与仿真,对比PID,积分分离以及滑模控制器。2.系统仿真结果 (完整程序运行后无水印) 3.核心程序与模型 版本:MATLAB2022a 4.系统原理简介PEM(质子交换膜)燃料电池作为一种高效的能量转换装置,在众多领域中展现出广泛的应…

4、二次创作

将文件拖入软件中 添加标题 设置参数 使用空格遮挡字幕 加水印 导入已经识别的字幕 修改字幕参数

【攻防世界】流量分析2

学到一个流量追踪的快捷操作可以快捷查看不同流的信息 还有找到了一款快捷分析工具

多态的引入--java进阶day02

1.多态的介绍我们以之前说的公司写业务为例子来理解多态,假设公司现在又要管理国外的业务,此时我们就需要重写一份关于国外的方案 从该业务上,也能看出接口的好处,我们直接写一个国外类实现接口,再按照规则重写方法即可,方便快捷接下来我们就简单写个菜单,用于判断是要国…