闲话 22.11.16

闲话

今天给我整了个好活 普通三维偏序的 \(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';
}



AGC056C

你需要构造一个长度为 \(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\) 等价。

列出我们已知的条件:

  1. \(\forall 1\le i \le m, \ d_{l_i - 1} = d_{r_i}\)
  2. \(\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]);
}



CF754E

给出一个字母阵 \(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';
	}
}
posted @ 2022-11-16 19:01  joke3579  阅读(90)  评论(6编辑  收藏  举报