闲话
今天给我整了个好活 普通三维偏序的 \(O(n\log n)\) 解法
那就要问了,形如 \(a_i\le a_j, b_i\le b_j, d_j\le c_i\le c_j\) 的加强版三维偏序有没有什么 \(o(n\log^2n)\) 的做法
如果有人搞出来了可以在这提交(
有什么好题当社论题材吗?
最近想写ARC148F来着
杂题
AGC056B
给定整数 \(n\) 以及 \(m\) 对整数。第 \(i\) 对整数为 \((l_i, r_i)\) 。请输出可以通过如下方式生成的整数序列 \(x = (x_1, x_2,\cdots,x_m)\) 的个数。答案对 \(998244353\) 取模。
生成方式:
- 取排列 \(p = (p_1, p_2,\cdots,p_n)\),满足其为一个 \(1\) 至 \(n\) 的排列。
- 对于任意 \(1\le i \le m\) 的 \(i\),令 \(x_i\) 为 \(p_{l_i}, p_{l_i + 1},\cdots ,p_{r_i}\) 中最大值对应的下标。即 \(p_{x_i} = \max\{p_{l_i}, p_{l_i + 1},\cdots, p_{r_i}\}\)。
\(2\le n\le 300,\ 1\le m\le \frac{n(n - 1)}2\)。
为啥这场 B 比 C 难啊
C 是个 2481 比这一段的 C 都简单 B 是个 nmd 3339 比这一段的 C 都难
A 是个 sb 构造,暂且不表
同一个 \(x\) 可能对应多个 \(p\),因此这样计数比较困难。
考虑反过来计数。
对于给定的 \(x\),我们将按照以下的方式构造 \(p\):
- 令 \(p = (-1,-1,\cdots,-1)\) 。
- 我们从 \(n\) 开始依次递减地考虑每个值 \(v\)。对于每个值,我们找到 \(v\) 能放的最左侧的位置,放进去。
计数可以通过这种方式生成的 \(p\) 。
设当前最值为 \(v\)。我们首先确定下标 \(m\),使得 \(p_m = v\)。对于所有包含 \(m\) 的区间 \(i\),有 \(x_i = m\)。删除这些包含 \(m\) 的区间后,我们可以分别考虑位于 \(m\) 左右两侧的区间。由于 \(m\) 为最左侧的可以放 \(v\) 的位置,右侧的数均小于左侧的数,这部分是和原问题等价但规模更小的子问题。
现在考虑左侧。我们令 \(k\) 为 \(m\) 左侧最大元素对应的下标。有:必定存在一个左侧区间同时包含 \(k\) 和 \(m\)。
考虑反证。如果左侧没有区间同时包含 \(k\) 和 \(m\),那我们可以令 \(p_k = v\),这必定是更优且满足要求的。这与假设矛盾,因此必定存在同时包含 \(k\) 和 \(m\) 的左侧区间。
因此,左侧区间所填的数的最大值必定大于等于 \(v\)。
考虑区间 dp。我们设 \(f(l,r,v)\) 为 \([l,r]\) 区间满足最大值大于等于 \(v\) 的方案数。可以通过枚举中点以及预处理区间最大值的方式转移。转移时加入后缀和即可快速得到最终答案。
时间复杂度 \(O(n^3)\)。
code
#include <bits/stdc++.h>
using namespace std;
using pii = pair<int,int>;
template <typename T> void get(T & x) {
x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x);
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
template <typename T1, typename T2> T1 min(T1 a, T2 b) { T1 _b = static_cast<T1>(b); return a < b ? a : b; }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 5e2 + 10;
const int mod = 998244353;
const int inv2 = (mod + 1) >> 1;
template <typename T1, typename T2> T1 add(T1 a, T2 b) { return (a += b) >= mod ? a - mod : a; }
template <typename T1, typename ...Args> T1 add(T1 a, Args ... b) { return add(a, add(b...)); }
struct FastMod { int m; ll b; void init(int _m) { m = _m; if (m == 0) m = 1; b = ((lll)1<<64) / m; } FastMod(int _m) { init(_m); } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod(mod);
int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }
template <typename T1, typename T2> T1 qp(T1 a, T2 b) { T1 ret = 1; for (; b > 0; a = mul(a, a), b >>= 1) if (b & 1) ret = mul(ret, a); return ret; }
int n, m, f[N][N][N], g[N][N][N];
pii v[N * (N - 1) >> 1];
int main() {
get(n, m); rep(i,1,m) get(v[i].first, v[i].second);
rep(i,0,n+1) rep(j,0,n+1) rep(k,0,n+1) g[i][j][k] = n + 1;
rep(i,1,m) rep(k,v[i].first,v[i].second) g[v[i].first][v[i].second][k] = min(g[v[i].first][v[i].second][k], v[i].first);
pre(l,n,1) rep(r,l+1,n) rep(k,l,r) g[l][r][k] = min( { g[l][r][k], g[l+1][r][k], g[l][r-1][k] } );
rep(l,1,n+1) rep(k,1,n+1) f[l][l-1][k] = 1;
pre(l,n,1) rep(r,l,n) pre(k,r,l) f[l][r][k] = add(f[l][r][k + 1], mul(f[l][k - 1][g[l][r][k]], f[k + 1][r][k + 1]));
cout << f[1][n][1] << '\n';
}
你需要构造一个长度为 \(n\) 、由 \(01\) 组成的字符串,同时需要满足 \(m\) 个条件。第 \(i\) 个条件由两个整数 \(l_i,\ r_i\) 给出,表示字符串位于 \([l_i,r_i]\) 区间的字符必须是相同数量的 \(0\) 和 \(1\)。
请输出满足所有条件且字典序最小的字符串。可以证明在题设条件下总存在至少一个字符串满足所有条件。
\(2\le n\le 10^6,\ 1\le m\le 2\times 10^5,\ 1\le l_i< r_i\le n, (r_i - l_i + 1) \bmod 2,\ (l_i,r_i)\neq(l_j,r_j) (i != j)\)
直接做不好做,考虑将条件转化。令 \(d_i\) 为 \(1\sim i\) 中 \(0\) 的个数减去 \(1\) 的个数。
容易发现,想要最小化原串的字典序,其实就是让 \(0\) 尽可能多,也就是让 \(d_i\) 尽可能大。这就转化为如何 \(d_i\) 的字典序最大。
我们发现,给定的条件 \((l_i,r_i)\) 可以表述为 \(d_{r_i} = d_{l_i - 1}\),因为 \([l_i,r_i]\) 段中 \(0,1\) 个数是相等的,体现在 \(d_i\) 上就是 \([l_i,r_i]\) 段有 \(0\) 的贡献。
同时根据定义可得 \(\forall \ 1< i \le n,\ |d_i - d_{i-1}| = 1\)。这与 \(\forall \ 1< i \le n,\ |d_i - d_{i-1}| \le 1\) 等价。
列出我们已知的条件:
- \(\forall 1\le i \le m, \ d_{l_i - 1} = d_{r_i}\)
- \(\forall \ 1< i \le n,\ |d_i - d_{i-1}| \le 1\)
我们需要使字典序最大,这启发我们采用差分约束系统求解本题。
对 \(1.\) 条件建 \((l_i - 1, r_i)\),边权为 \(0\) 的双向边;对 \(2.\) 条件建 \((i - 1, i)\),边权为 \(1\) 的双向边。
由于边权的特殊性,我们可以通过 01bfs 求解所有 \(d_i\)。这给出了构造方案。
总时间复杂度 \(O(n +m)\)。
code
#include <bits/stdc++.h>
using namespace std;
template <typename T> void get(T & x) {
x = 0; char ch = getchar(); bool f = false; while (ch < '0' or ch > '9') f = f or ch == '-', ch = getchar();
while ('0' <= ch and ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); f && (x = -x);
} template <typename T, typename ... Args> void get(T & a, Args & ... b) { get(a); get(b...); }
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 1e6 + 10;
int n, m, l, r;
vector <pair<int,int> > g[N];
int dis[N];
deque<int> que;
void bfs(int st) {
memset(dis, -1, sizeof dis);
dis[st] = 0; que.push_back(st);
while (que.size()) {
int u = que.front(); que.pop_front();
for (auto [v, w] : g[u]) {
if (dis[v] == -1) {
dis[v] = dis[u] + w;
if (w == 0) que.push_front(v);
else que.push_back(v);
}
}
}
}
int main() {
get(n, m);
rep(i,1,m) get(l, r), g[r].emplace_back(l - 1, 0), g[l - 1].emplace_back(r, 0);
rep(i,1,n) g[i - 1].emplace_back(i, 1), g[i].emplace_back(i - 1, 1);
bfs(0);
rep(i,1,n) cout << (dis[i] < dis[i - 1]);
}
给出一个字母阵 \(A\),再给出一个部分字母没有确定的询问阵 \(B\),求出左上角为位置 \((i,j)\) 时能否匹配 \((A[(i+x) \text{mod } n][(i+y) \text{mod }m]=B[i][j])\),输出 \(01\) 矩阵表示答案。
\(1\le n, m, r, c \le 400\)。
昨天 WintersRain 发了个博客 vp 390 (Div. 2)
然后我看着他们没写这题 然后水一下
首先考虑朴素的 \(O(n^4)\) 如何实现。令 \(P\) 为 \(n\times m\) 的矩阵,\(T\) 为 \(r\times c\) 的矩阵。
memset(ans, true, sizeof(ans));
for (int k = 1; k <= r; ++ k) for (int l = 1; l <= c; ++ l) if (T[k][l] != '?')
for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j)
ans[(i - k + n) % n][(j - l + m) % m] &= (T[k][l] == P[i][j]);
发现最终我们需要对答案数组的整行进行有条件的与操作。这使我们想到使用 bitset 优化进程。
对 \(P\) 矩阵中每行使用 26 个 bitset ,第 \(i\) 位保存 \(\text a\sim \text z\) 是否存在于这一位。我们发现朴素实现的第 \(4\) 层循环可以使用 bitset 表示。
因此采用 bitset 优化后可以做到 \(O(\frac {nmrc} {w})\),足以通过本题。
本题的思想类似于 CF914F,二者都是通过 bitset 进行带通配符的字符串匹配。
同时,本题还有采用多项式的解法,方式形如本题。时间复杂度为 \(O(nmc\log n)\)。由于常数过大,在实际测评中远不如 bitset 做法优秀。
以及这题 \(r,c\) 可能远大于 \(n,m\),注意直接取模。
code
#include <bits/stdc++.h>
using namespace std;
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
const int N = 400 + 10;
int n, m, r, c;
char str[N];
bitset<N> ans[N], mask[26][N];
int main() {
ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n >> m;
rep(i,1,n) {
ans[i].set();
cin >> str + 1;
rep(j,1,m) mask[str[j]-'a'][i].set(j);
} cin >> r >> c;
rep(i,1,r) {
cin >> str + 1;
rep(j,1,c) if (str[j] != '?') rep(k,1,n)
ans[(k - i % n + n) % n + 1] &= (mask[str[j] - 'a'][k] >> (j - 1 + m) % m) | (mask[str[j] - 'a'][k] << m - (j - 1 + m) % m);
}
rep(i,1,n) {
rep(j,1,m) cout << ans[i][j];
cout << '\n';
}
}