AtCoder Beginner Contest 215
E - Chain Contestant
题意
给出一个由 10 10 1 0 种大写字母 A ∼ J A\sim J A ∼ J 组成的字符串 S S S ,长度为 N N N ,求 S S S 有多少个下标序列 满足下列条件:
令下标序列所对应的 S S S 的子序列为 T T T ,满足同一种字母在 T T T 中都是连续出现的,如:AAABBCCC
满足条件,但 AABBACCC
就不满足条件,因为字母 A
不连续。
数据范围:N ⩽ 1000 N\leqslant 1000 N ⩽ 1 0 0 0
思路
看到字母的类型只有10种,而且每一个状态下要保证之前没有出现过该数字,所以可以用dp的一维保存之前出现过的数字,由于最后一位是可以连续的,所以dp还有一维存储最后一位的数字。
先将字母对应成数字 0 ∼ 9 0\sim9 0 ∼ 9 。
设 f ( i , j , U ) f(i, j, U) f ( i , j , U ) ,表示前 i i i 个字符组成的子序列中,最后一位为 j j j ,且使用子序列中包含了集合 U U U 中的数字。
设 S i = x S_i=x S i = x ,则有如下转移:
f ( i , j , U ) = f ( i − 1 , j , U ) f(i, j, U) = f(i-1, j, U) f ( i , j , U ) = f ( i − 1 , j , U ) 代表第 i i i 位不取。
f ( i , x , U ) + = f ( i − 1 , x , U ) f(i, x, U) += f(i-1, x, U) f ( i , x , U ) + = f ( i − 1 , x , U ) 代表取第 i i i 位,且是上一位取值也是 x x x 。
f ( i , x , V ∪ { x } ) + = f ( i − 1 , j , V ) , ( x ∉ V ) f(i, x, V\cup\{x\}) += f(i-1, j, V), (x\not\in V) f ( i , x , V ∪ { x } ) + = f ( i − 1 , j , V ) , ( x ∈ V ) 代表取第 i i i 位,且是第一次取 x x x 。
f ( i , x , { x } ) + = 1 f(i, x, \{x\}) += 1 f ( i , x , { x } ) + = 1 代表只取第 i i i 位。
则,ANS = ∑ ∀ j , U f ( N , j , U ) \displaystyle \text{ANS} = \sum_{\forall j, U} f(N, j, U) ANS = ∀ j , U ∑ f ( N , j , U ) 。
总复杂度 O ( 10 × 2 10 × N ) O(10\times2^{10}\times N) O ( 1 0 × 2 1 0 × N ) 。
由于dp的第一维可以滚掉,所以就少开了一维。
点击显/隐代码
#include <bits/stdc++.h>
#define db double
#define ll long long
#define int ll
#define vi vector<int>
#define vii vector<vi >
#define pii pair<int, int>
#define vp vector<pii >
#define vip vector<vp >
#define mkp make_pair
#define pb push_back
#define Case(x) cout << "Case #" << x << ": "
using namespace std;
const int INF = 0x3f3f3f3f;
const int P = 998244353;
const int N = 1e3 + 10;
int f[10][1<<10], tmp[10][1<<10];
signed main(){
#ifdef _DEBUG
// FILE *file = freopen("out", "w", stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
string s;
cin >> s;
for (int i = 0; i < n; i++) {
int x = s[i] - 'A';
memcpy(tmp, f, sizeof(f));
for (int k = 0; k < (1<<10); k++) {
f[x][k] = (f[x][k] + tmp[x][k]) % P;
if ((k >> x & 1) == 0) {
for (int j = 0; j < 10; j++) {
f[x][k | (1<<x)] = (f[x][k | (1<<x)] + tmp[j][k]) % P;
}
}
}
f[x][1<<x] = (f[x][1<<x] + 1) % P;
}
int ans = 0;
for (int j = 0; j < 10; j++) {
for (int k = 0; k < (1<<10); k++) {
ans = (ans + f[j][k]) % P;
}
}
cout << ans << '\n';
return 0;
}
F - Dist Max 2
题意
给出 N N N 个二维坐标 ( x i , y i ) (x_i, y_i) ( x i , y i ) ,定义两个点 i , j i, j i , j 的距离为 min ( ∣ x i − x j ∣ , ∣ y i − y j ∣ ) \min(|x_i-x_j|, |y_i-y_j|) min ( ∣ x i − x j ∣ , ∣ y i − y j ∣ ) 。
(切比雪夫距离是取 max \max max )
找到这 N N N 个点中两个点的距离的最大值。
数据范围:N ⩽ 2 × 1 0 5 N\leqslant 2\times 10^5 N ⩽ 2 × 1 0 5 。
思路
求最大化最小值,考虑二分答案的方法,由于每个点有 x , y x, y x , y 两个参数,故可以先对 x x x 从小到大排序。
设答案为 M M M ,则一定存在 i > j i > j i > j 使得,x i − x j ⩾ M x_i-x_j \geqslant M x i − x j ⩾ M 并且 ∣ y i − y j ∣ ⩾ M |y_i-y_j| \geqslant M ∣ y i − y j ∣ ⩾ M ,
由于当前 x x x 有序,则可以从小到大顺次枚举,对于当前枚举到的 i i i ,如果 j j j 满足 x i − x j ⩾ M x_i-x_j\geqslant M x i − x j ⩾ M ,则有 ∀ k ⩽ j , x i − x k ⩾ M \forall k \leqslant j, x_i-x_k\geqslant M ∀ k ⩽ j , x i − x k ⩾ M ,故 1 ∼ j 1\sim j 1 ∼ j 这些元素的 x x x 值都已经满足条件,只需找到满足条件的 y y y 值,由于满足条件的值一定是边界值,也就是最大或最小值,于是再维护 1 ∼ j 1\sim j 1 ∼ j 对应 y y y 坐标的最大值和最小值,最终判断是否有满足条件的 y y y 值即可。
总复杂度 O ( N l o g N ) O(NlogN) O ( N l o g N ) 。
点击显/隐代码
#include <bits/stdc++.h>
#define db double
#define ll long long
#define vi vector<int>
#define vii vector<vi >
#define pii pair<int, int>
#define vp vector<pii >
#define vip vector<vp >
#define mkp make_pair
#define pb push_back
#define Case(x) cout << "Case #" << x << ": "
using namespace std;
const int INF = 0x3f3f3f3f;
const int P = 998244353;
signed main(){
#ifdef _DEBUG
// FILE *file = freopen("out", "w", stdout);
#endif
int n;
scanf("%d", &n);
vp a(n);
for (int i = 0; i < n; i++) {
scanf("%d %d", &a[i].first, &a[i].second);
}
sort(a.begin(), a.end());
int l = 0, r = 1e9;
while (l <= r) {
int mid = (l + r) >> 1;
int fg = 0, mx = 0, mn = 1e9;
queue<pii> q;
for (auto p : a) {
while (!q.empty()) {
if (p.first - q.front().first < mid) break;
mx = max(mx, q.front().second);
mn = min(mn, q.front().second);
q.pop();
}
if (p.second - mn >= mid || mx - p.second >= mid) {
fg = 1;
break;
}
q.push(p);
}
if (fg) l = mid + 1;
else r = mid - 1;
}
printf("%d\n", r);
return 0;
}