2022杭电多校(三)
文章目录
一、比赛小结
二、题目分析及解法(基础题)
1001、Equipment Upgrade
题目链接:Problem - 7162 (hdu.edu.cn)
题意:
一共有 n n n 级,在第 i i i 级时,升级到 i + 1 i+1 i+1 级的概率为 p i p_i pi ,且要被花费 c i c_i ci 元,并且还有 ( 1 − p i ) ω j ∑ k = 1 i ω k \displaystyle (1-p_i)\frac{\omega_j}{\sum_{k=1}^i\omega_k} (1−pi)∑k=1iωkωj 的概率降到第 i − j i-j i−j 级,求从 0 0 0 级升到 n n n 级的期望花费。
题解:
我们先来推下式子:
E n = 0 E_n=0 En=0
E i = c i + p i ⋅ E i + 1 + 1 − p i ∑ j = 1 i ω j ⋅ ( ∑ j = 1 i ω j ⋅ E i − j ) \displaystyle E_i=c_i+p_i \cdot E_{i+1}+\frac{1-p_i}{\sum_{j=1}^i\omega_j}\cdot (\sum_{j=1}^i\omega_j\cdot E_{i-j}) Ei=ci+pi⋅Ei+1+∑j=1iωj1−pi⋅(j=1∑iωj⋅Ei−j)
E i + 1 = E i − c i − 1 − p i ∑ j = 1 i ω j ⋅ ( ∑ j = 1 i ω j ⋅ E i − j ) p i \displaystyle E_{i+1}=\frac{E_i-c_i-\frac{1-p_i}{\sum_{j=1}^i\omega_j}\cdot (\sum_{j=1}^i{\omega_j\cdot E_{i-j}})}{p_i} Ei+1=piEi−ci−∑j=1iωj1−pi⋅(∑j=1iωj⋅Ei−j)
然后设 E i = a i ⋅ E 0 + b i E_i=a_i\cdot E_0+b_i Ei=ai⋅E0+bi ,则有:
a 0 = 1 , b 0 = 0 a_0=1, \ b_0=0 a0=1, b0=0
a i + 1 = a i − 1 − p i ∑ j = 1 i ω j ⋅ ( ∑ j = 1 i ω j ⋅ a i − j ) p i \displaystyle a_{i+1}=\frac{a_i-\frac{1-p_i}{\sum_{j=1}^i\omega_j}\cdot (\sum_{j=1}^i\omega_j \cdot a_{i-j})}{p_i} ai+1=piai−∑j=1iωj1−pi⋅(∑j=1iωj⋅ai−j)
b i + 1 = b i − c i − 1 − p i ∑ j = 1 i ω j ⋅ ( ∑ j = 1 i ω j ⋅ b i − j ) p i \displaystyle b_{i+1}=\frac{b_i-c_i-\frac{1-p_i}{\sum_{j=1}^i\omega_j}\cdot (\sum_{j=1}^i\omega_j\cdot b_{i-j})}{p_i} bi+1=pibi−ci−∑j=1iωj1−pi⋅(∑j=1iωj⋅bi−j)
这个递推有卷积的形式,因此可以用 分治+NTT 来做,分治NTT已经变成常识了(悲
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace poly {
typedef long long ll;
const int mod = 998244353;
const int maxn = 1e6 + 10;
ll invi[maxn];
// 记得先init()!!!!!!!!!
void init() {
invi[1] = 1;
for (int i = 2; i < maxn; i++) {
invi[i] = (mod - mod / i) * invi[mod % i] % mod;
}
}
// 快速幂
ll quickpow(ll x, ll y) {
ll ans = 1;
while (y) {
if (y & 1) ans = ans * x % mod;
y >>= 1;
x = x * x % mod;
}
return ans;
}
// 二次剩余
ll modsqrt(ll x) {
if (mod == 2) return x % mod;
if (quickpow(x, mod - 1 >> 1) != 1) return -1;
ll ans = 0;
if (mod % 4 == 3)
ans = quickpow(x, mod + 1 >> 2);
else {
ll b;
for (b = rand() % mod; quickpow(b, mod - 1 >> 1) == 1; b = rand() % mod)
;
ll i = mod - 1 >> 1, k = 0;
do {
i >>= 1;
k >>= 1;
if ((quickpow(x, i) * quickpow(b, k) + 1) % mod == 0) k += (mod - 1 >> 1);
} while (i % 2 == 0);
ans = quickpow(x, i + 1 >> 1) * quickpow(b, k >> 1) % mod;
}
if (ans * 2 > mod) ans = mod - ans;
return ans;
}
// 蝴蝶变换
void change(ll* a, int len) {
static int rev[maxn];
rev[0] = 0;
for (int i = 0; i < len; i++) {
rev[i] = (rev[i >> 1] >> 1);
if (i & 1) rev[i] |= len >> 1;
}
for (int i = 0; i < len; i++)
if (i < rev[i]) swap(a[i], a[rev[i]]);
}
// flag=1 -> NTT , flag=-1 -> INTT , O(nlogn)
void NTT(ll* a, int len, int flag) {
change(a, len);
for (int h = 2; h <= len; h <<= 1) {
ll uni = quickpow(3, (mod - 1) / h); // 3 为 mod = 998244353 的原根
if (flag == -1) uni = quickpow(uni, mod - 2);
for (int i = 0; i < len; i += h) {
ll w = 1;
for (int j = i; j < i + h / 2; j++) {
ll u = a[j] % mod;
ll v = w * a[j + h / 2] % mod;
a[j] = (u + v) % mod;
a[j + h / 2] = (u - v + mod) % mod;
w = w * uni % mod;
}
}
}
if (flag == -1) {
ll inv = quickpow(len, mod - 2);
for (int i = 0; i < len; i++) a[i] = a[i] * inv % mod;
}
}
// 多项式乘法,n m 分别为 a b 最高次数
void polymul(ll* a, ll* b, ll* ans, int n, int m) {
static ll tpa[maxn], tpb[maxn];
memcpy(tpa, a, sizeof(ll) * (n + 1));
memcpy(tpb, b, sizeof(ll) * (m + 1));
int len = 1;
for (; len < n + 1; len <<= 1) // n + m + 1 !!! 本题特殊
;
memset(tpa + n + 1, 0, sizeof(ll) * (len - n));
memset(tpb + m + 1, 0, sizeof(ll) * (len - m));
NTT(tpa, len, 1);
NTT(tpb, len, 1);
for (int i = 0; i <= len; i++) ans[i] = tpa[i] * tpb[i] % mod;
NTT(ans, len, -1);
}
// // 多项式求逆,ans为最终答案数组, O(nlogn)
// void polyinv(ll* a, ll* ans, int len) {
// static ll tp[maxn];
// memset(tp, 0, sizeof(ll) * (len << 1));
// memset(ans, 0, sizeof(ll) * (len << 1));
// ans[0] = quickpow(a[0], mod - 2);
// for (int h = 2; h <= len; h <<= 1) {
// memcpy(tp, a, sizeof(ll) * h);
// NTT(tp, h << 1, 1);
// NTT(ans, h << 1, 1);
// for (int i = 0; i < (h << 1); i++)
// ans[i] = ans[i] * ((2 + mod - tp[i] * ans[i] % mod) % mod) % mod;
// NTT(ans, h << 1, -1);
// memset(ans + h, 0, sizeof(ll) * h);
// }
// }
// // 多项式除法,a / b = ans1, a % b = ans2
// void polydiv(ll* a, ll* b, ll* ans1, ll* ans2, int n, int m) {
// static ll tp0[maxn];
// int len = 1;
// for (; len < n - m + 1; len <<= 1)
// ;
// memset(tp0, 0, sizeof(ll) * len);
// for (int i = 0; i <= n; i++) ans1[n - i] = a[i];
// for (int i = 0; i <= m; i++) ans2[m - i] = b[i];
// for (int i = n - m + 1; i <= m; i++) ans2[i] = 0;
// polyinv(ans2, tp0, len);
// polymul(ans1, tp0, tp0, n, n - m);
// for (int i = 0; i <= n - m; i++) ans1[i] = tp0[n - m - i];
// polymul(b, ans1, ans2, m, n - m);
// for (int i = 0; i < m; i++) ans2[i] = (a[i] - ans2[i] + mod) % mod;
// }
// // 多项式求导
// void qiudao(ll* a, ll* ans, int len) {
// for (int i = 1; i < len; i++) ans[i - 1] = a[i] * i % mod;
// ans[len - 1] = 0;
// }
// // 多项式积分
// void jifen(ll* a, ll* ans, int len) {
// for (int i = len - 1; i; i--) ans[i] = a[i - 1] * invi[i] % mod;
// ans[0] = 0; // 积分常数C
// }
// // 多项式ln,a[0]!=0,O(nlogn)
// void polyln(ll* a, ll* ans, int len) {
// static ll tp1[maxn];
// qiudao(a, tp1, len);
// memset(tp1 + len, 0, sizeof(ll) * len);
// polyinv(a, ans, len);
// NTT(ans, len << 1, 1);
// NTT(tp1, len << 1, 1);
// for (int i = 0; i < (len << 1); i++) tp1[i] = tp1[i] * ans[i] % mod;
// NTT(tp1, len << 1, -1);
// jifen(tp1, ans, len);
// }
// // 多项式exp,a[0]==0,O(nlogn)
// void polyexp(ll* a, ll* ans, int len) {
// static ll tp2[maxn];
// memset(tp2, 0, sizeof(ll) * (len << 1));
// memset(ans, 0, sizeof(ll) * (len << 1));
// ans[0] = 1;
// for (int h = 2; h <= len; h <<= 1) {
// polyln(ans, tp2, h);
// tp2[0] = ((a[0] + 1) % mod - tp2[0] + mod) % mod;
// for (int i = 1; i < h; i++) tp2[i] = (a[i] - tp2[i] + mod) % mod;
// memset(tp2 + h, 0, sizeof(ll) * h);
// NTT(ans, h << 1, 1);
// NTT(tp2, h << 1, 1);
// for (int i = 0; i < (h << 1); i++) ans[i] = ans[i] * tp2[i] % mod;
// NTT(ans, h << 1, -1);
// memset(ans + h, 0, sizeof(ll) * h);
// }
// }
// // 多项式开根,O(nlogn)
// void polysqrt(ll* a, ll* ans, int len) {
// static ll tp3[maxn], tp4[maxn];
// memset(tp3, 0, sizeof(ll) * (len << 1));
// memset(tp4, 0, sizeof(ll) * (len << 1));
// memset(ans, 0, sizeof(ll) * (len << 1));
// ans[0] = modsqrt(a[0]); // 二次剩余
// ll inv2 = quickpow(2, mod - 2);
// for (int h = 2; h <= len; h <<= 1) {
// polyinv(ans, tp3, h);
// memcpy(tp4, a, sizeof(ll) * h);
// NTT(tp3, h << 1, 1);
// NTT(tp4, h << 1, 1);
// NTT(ans, h << 1, 1);
// for (int j = 0; j < (h << 1); j++)
// ans[j] =
// (ans[j] * ans[j] % mod + tp4[j]) % mod * inv2 % mod * tp3[j] % mod;
// NTT(ans, h << 1, -1);
// memset(ans + h, 0, sizeof(ll) * h);
// }
// }
} // namespace poly
typedef long long ll;
const int maxn = 1e6 + 10;
const int mod = 998244353;
int n;
int c[maxn], w[maxn];
int p[maxn], pre[maxn], invp[maxn];
int ea[maxn], eb[maxn];
int fa[maxn], fb[maxn];
int ta[maxn], tb[maxn], tw[maxn];
void solve(int l, int r) {
if (l == r) {
if (l) {
ea[l + 1] = ((ea[l] - (1ll - p[l]) * fa[l] % mod * pre[l]) % mod + mod) *
invp[l] % mod;
eb[l + 1] =
((eb[l] - c[l] - (1ll - p[l]) * fb[l] % mod * pre[l]) % mod + mod) *
invp[l] % mod;
}
return;
}
int mid = (l + r) >> 1;
solve(l, mid);
// !!!important
for (int i = 0; i < maxn && i <= 2 * (r - l); i++) ta[i] = tb[i] = tw[i] = 0;
for (int i = l; i <= mid; i++) ta[i - l] = ea[i], tb[i - l] = eb[i];
for (int i = 1; i <= r - l; i++) tw[i] = w[i];
poly::polymul(ta, tw, ta, r - l, r - l);
poly::polymul(tb, tw, tb, r - l, r - l);
for (int i = mid + 1; i <= r; i++) {
fa[i] = (fa[i] + ta[i - l]) % mod;
fb[i] = (fb[i] + tb[i - l]) % mod;
}
solve(mid + 1, r);
}
signed main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
poly::init();
int _;
cin >> _;
while (_--) {
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x >> c[i];
p[i] = x * poly::invi[100] % mod;
invp[i] = 100ll * poly::invi[x] % mod;
}
for (int i = 1; i < n; i++) {
cin >> w[i];
pre[i] = (pre[i - 1] + w[i]) % mod;
}
for (int i = 1; i < n; i++) pre[i] = poly::quickpow(pre[i], mod - 2);
for (int i = 0; i <= n; i++) fa[i] = fb[i] = 0;
ea[0] = 1, eb[0] = 0;
ea[1] = 1, eb[1] = (mod - c[0]) % mod;
solve(0, n - 1);
int ans = (mod - eb[n]) * poly::quickpow(ea[n], mod - 2) % mod;
cout << (ans + mod) % mod << endl;
}
return 0;
}
1002、Boss Rush
题目链接:Problem - 7163 (hdu.edu.cn)
题意:
小Q有 n n n 个技能,每个技能点击后需要再过 t i t_i ti 时间才能点击其他技能,对于每个技能 i i i ,点击后的 l e n i len_i leni 时间内均会造成伤害 d i , j d_{i, j} di,j ,boss 的血量为 H H H ,问最早多久可以打败 boss
题解:
其实感觉题目有些问题,题解的意思似乎是不考虑顺序,这里的 n n n 比较小 ( n ≤ 15 ) (n\le 15) (n≤15) ,可以乱搞,二分+状压可以轻松做出
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 20;
const int maxm = 1e5 + 5;
int t[maxm], len[maxm];
int d[maxn][maxm];
int tim[1 << maxn], dp[1 << maxn];
int n, hp;
bool check(int x) {
memset(dp, -1, sizeof(dp));
dp[0] = 0;
for (int st = 0; st < 1 << n; st++) {
int w = dp[st];
if (w >= hp) return 1;
if (w < 0 || tim[st] > x) continue;
for (int i = 0; i < n; i++)
if (!(st >> i & 1))
if (tim[st] + len[i] - 1 <= x)
dp[st | (1 << i)] = max(dp[st | (1 << i)], w + d[i][len[i] - 1]);
else
dp[st | (1 << i)] = max(dp[st | (1 << i)], w + d[i][x - tim[st]]);
}
return 0;
}
signed main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n >> hp;
int ans = -1;
int l = 0, r = 0;
for (int i = 0; i < n; i++) {
cin >> t[i] >> len[i];
r += t[i] + len[i];
for (int j = 0; j < len[i]; j++) cin >> d[i][j];
for (int j = 1; j < len[i]; j++) d[i][j] += d[i][j - 1];
}
for (int st = 1; st < (1 << n); st++)
tim[st] = tim[st - (st & -st)] + t[__builtin_ctz(st & -st)];
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid))
r = (ans = mid) - 1;
else
l = mid + 1;
}
cout << ans << endl;
}
return 0;
}
1003、Cyber Language
题目链接:Problem - 7164 (hdu.edu.cn)
题意:
给一堆句子,句子中包含几个单词,输出单词的首字母大写
题解:
~~
代码:
#include <bits/stdc++.h>
using namespace std;
string s;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int _;
cin >> _;
getchar();
while (_--) {
getline(cin, s);
int len = s.length();
for (int i = 0; i < len; i++)
if (i == 0 || s[i - 1] == ' ') cout << (char)toupper(s[i]);
cout << endl;
}
return 0;
}
1008、Laser Alarm
题目链接: Problem - 7169 (hdu.edu.cn)
题意:
空间中有 n n n 条线段 n ≤ 50 n\le 50 n≤50 ,求一个与线段交点最多的平面
题解:
由于
n
n
n 比较小,所以可以乱搞,三点确定一个平面,所以我们枚举所有三点对即可,然后遍历计数
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
struct node {
int x, y, z;
node(int _x = 0, int _y = 0, int _z = 0) : x(_x), y(_y), z(_z) {}
node operator-(const node& p) const {
return node(x - p.x, y - p.y, z - p.z);
}
node operator*(const node& p) const {
return node(y * p.z - z * p.y, z * p.x - x * p.z, x * p.y - y * p.x);
}
int operator^(const node& p) const { return x * p.x + y * p.y + z * p.z; }
bool operator==(const node& p) const {
return x == p.x && y == p.y && z == p.z;
}
} p[maxn * 2];
int ptoplane(const node& a, const node& b, const node& c, const node& p) {
return ((b - a) * (c - a)) ^ (p - a);
}
bool colinear(const node& a, const node& b, const node& p) {
node t = (a - b) * (b - p);
return !t.x && !t.y && !t.z;
}
int n;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n;
for (int i = 1; i <= n * 2; i++) cin >> p[i].x >> p[i].y >> p[i].z;
int ans = 1;
for (int i = 1; i <= n * 2; i++) {
for (int j = 1; j < i; j++) {
if (p[i] == p[j]) continue;
for (int k = 1; k < j; k++) {
if (p[i] == p[j] || p[i] == p[k] || p[j] == p[k]) continue;
if (colinear(p[i], p[j], p[k])) continue;
int now = 0;
for (int l = 1; l <= n; l++) {
int x = ptoplane(p[i], p[j], p[k], p[l * 2 - 1]);
int y = ptoplane(p[i], p[j], p[k], p[l * 2]);
if (!x || !y || (x < 0 && y > 0) || (x > 0 && y < 0)) now++;
}
ans = max(ans, now);
}
int now = 0;
for (int k = 1; k <= n; k++) {
if (colinear(p[i], p[j], p[k * 2 - 1]) ||
colinear(p[i], p[j], p[k * 2]))
now++;
}
ans = max(ans, now);
}
}
cout << ans << endl;
}
return 0;
}
1009、Package Delivery
题目链接:Problem - 7170 (hdu.edu.cn)
题意:
小Q有 n n n 个包裹,每个包裹都有一个区间时间,小Q必须在截至前拿走包裹,每次小Q最多可以拿 k k k 个,问小Q最少去邮局多少次
题解:
贪心即可,或者说尽量拖延就行,关注每个包裹的截至时间,在这个时间拿走包裹,顺便拿走其他截至时间早的就行。
下面这个算法看似是 O ( n 2 ) O(n^2) O(n2) 的,实际上线性时间内可以通过
代码:
#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int maxn = 1e5 + 5;
int n, k;
int ql[maxn], qr[maxn];
pii p[maxn];
bool vis[maxn];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
memset(vis, 0, sizeof vis);
priority_queue<pii, vector<pii>, greater<pii>> q;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> p[i].first >> p[i].second;
ql[i] = qr[i] = i;
}
sort(ql + 1, ql + 1 + n,
[](int a, int b) { return p[a].first < p[b].first; });
sort(qr + 1, qr + 1 + n,
[](int a, int b) { return p[a].second < p[b].second; });
int res = 0;
for (int i = 1, j = 1; i <= n; i++) {
if (vis[qr[i]]) continue;
while (j <= n && p[ql[j]].first <= p[qr[i]].second) {
q.push({p[ql[j]].second, ql[j]});
j++;
}
for (int l = 1; l <= k; l++) {
if (q.empty()) break;
vis[q.top().second] = true;
q.pop();
}
res++;
}
cout << res << endl;
}
}
1011、Taxi
题目链接:Problem - 7172 (hdu.edu.cn)
题意:
有 n n n 个城镇,每个城镇有一个坐标,小Q有张VIP卡,假如小Q从 ( x ′ , y ′ ) (x',y') (x′,y′) 前往 ( x k , y k ) (x_k,y_k) (xk,yk) ,那么VIP卡可以帮他报销 min { ∣ x ′ − x k ∣ + ∣ y ′ − y k ∣ , ω k } \min \{|x'-x_k|+|y'-y_k|, \omega_k \} min{∣x′−xk∣+∣y′−yk∣,ωk} 元。小Q想要去充分利用他的VIP卡,给出 q q q 个询问,每次询问给出一个坐标,要求找到VIP打折最多的一个地点。
题解:
这题用到了一些关于切比雪夫距离的一些技巧。没有 ω \omega ω 的限制的话,其实是一道很经典的问题。
max { ∣ x ′ − x i ∣ + ∣ y ′ − y i ∣ } = max { max { x ′ − x i , − x ′ + x i } + max { y ′ − y i , − y ′ + y i } } = max { [ ( x ′ + y ′ ) + ( − x i − y i ) ] , [ ( x ′ − y ′ ) + ( − x i + y i ) ] , [ ( − x ′ + y ′ ) + ( x i − y i ) ] , [ ( − x ′ − y ′ ) + ( x i + y i ) ] } \max \{ |x'-x_i|+|y'-y_i| \} \\ =\max \{ \max\{x'-x_i,-x'+x_i\}+\max\{ y'-y_i, -y'+y_i\}\}\\=\max\{[(x'+y')+(-x_i-y_i)],[(x'-y')+(-x_i+y_i)],[(-x'+y')+(x_i-y_i)],[(-x'-y')+(x_i+y_i)]\} max{∣x′−xi∣+∣y′−yi∣}=max{max{x′−xi,−x′+xi}+max{y′−yi,−y′+yi}}=max{[(x′+y′)+(−xi−yi)],[(x′−y′)+(−xi+yi)],[(−x′+y′)+(xi−yi)],[(−x′−y′)+(xi+yi)]} 于是,记录 ( − x i − y i ) , ( − x i + y i ) , ( x i − y i ) , ( x i + y i ) (-x_i-y_i), \ (-x_i+y_i), \ (x_i-y_i), \ (x_i+y_i) (−xi−yi), (−xi+yi), (xi−yi), (xi+yi) 的最大值即可 O ( 1 ) O(1) O(1) 内查询,现考虑加入 ω \omega ω 的影响
先按 ω \omega ω 的大小对这些点排一个序,并记录一下区间后缀的距离最大值,进行分治二分即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int n, q;
struct node {
int x, y;
int w;
} p[maxn];
int a[maxn], b[maxn], c[maxn], d[maxn];
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y >> p[i].w;
sort(p + 1, p + n + 1,
[](const node &a, const node &b) { return a.w < b.w; });
a[n + 1] = b[n + 1] = c[n + 1] = d[n + 1] = -inf;
for (int i = n; i > 0; i--) {
a[i] = max(a[i + 1], -p[i].x - p[i].y);
b[i] = max(b[i + 1], -p[i].x + p[i].y);
c[i] = max(c[i + 1], p[i].x - p[i].y);
d[i] = max(d[i + 1], p[i].x + p[i].y);
}
while (q--) {
int x, y;
cin >> x >> y;
int ans = 0;
int l = 1, r = n, mid;
while (l <= r) {
mid = (l + r) >> 1;
int tmp = max(max(x + y + a[mid], x - y + b[mid]),
max(-x + y + c[mid], -x - y + d[mid]));
if (p[mid].w < tmp) {
l = mid + 1;
ans = max(ans, p[mid].w);
} else {
r = mid - 1;
ans = max(ans, tmp);
}
}
cout << ans << endl;
}
}
return 0;
}
1012、Two Permutations
题目链接:Problem - 7173 (hdu.edu.cn)
题意:
给出两个排列和一个序列,求两个排列组合成这个序列的方案数,两个排列的长均为 n n n ,序列长为 2 n 2n 2n ,每次总是将排列 P 、 Q P、Q P、Q 的最左端放进序列 S S S 的最右侧。 n ≤ 3 × 1 0 5 n\le 3\times 10^5 n≤3×105
题解:
很显然是一道 dp 题,那么该怎么去 dp 呢。表面上看,状态似乎是 n 2 n^2 n2 ,但实际上,有很多无效的状态,根据数据规模,其状态应该是 O ( n ) O(n) O(n) 级别的。设 d p i , j dp_{i, j} dpi,j 表示匹配至序列 S S S 的第 i i i 位, 并且最后一次匹配来源于 j = 1 / 2 j=1 / 2 j=1/2 ,那么有递归式: d p i , j = j u d g e 1 × d p i − 1 , j + j u d g e 2 × d p i − 1 , j ⊕ 1 dp_{i, j}=judge_1\times dp_{i-1, j}+judge_2\times dp_{i-1, j\oplus 1} dpi,j=judge1×dpi−1,j+judge2×dpi−1,j⊕1
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
const int maxn = 3e5 + 5;
int p[maxn], q[maxn];
int s[maxn * 2];
int n;
int dp[2 * maxn][2];
int dfs(int i, int j, int flag) {
// 匹配完 p_i, q_j 的方案数, flag 0 \ 1 代表从 p \ q 进入新匹配
if (i <= 0 && j <= 0) return 1;
if (dp[i + j][flag] != -1) return dp[i + j][flag];
int ans = 0;
if (p[i] == s[i + j] && i > 0) ans = (ans + dfs(i - 1, j, 0)) % mod;
if (q[j] == s[i + j] && j > 0) ans = (ans + dfs(i, j - 1, 1)) % mod;
return dp[i + j][flag] = ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int _;
cin >> _;
while (_--) {
memset(dp, -1, sizeof(dp));
cin >> n;
for (int i = 1; i <= n; i++) cin >> p[i];
for (int i = 1; i <= n; i++) cin >> q[i];
for (int i = 1; i <= 2 * n; i++) cin >> s[i];
cout << dfs(n, n, 0) << endl;
}
return 0;
}
二、题目分析及解法(进阶题)
不会X