stream pack 1.
收录动态规划题
1. LuoguP11189 -「KDOI-10」水杯降温
tag:二分、状态设计;link。
发现只用操作 \(1\) 就能够完成当且仅当:
- \(\forall i,a_i\leq 0\);
- \(a_i-\sum_{j\in son_i}a_j=0\)。
设 \(b_i=a_i-\sum_{j\in son_i}a_j\),考虑操作 \(2\) 对于 \(b\) 数组的影响。设 \(c_i\) 表示点 \(i\) 上操作 \(2\) 的次数,\(b,c\) 的关系并不简单,但是若设 \(f_i\) 表示 \(i\to 1\) 上 \(c\) 的和,那么有简单的式子:
猜测 \(f_i\) 的取值是一个区间,其实真是。
对于叶子,\(f_i\in[\max(0, -b_i),+\inf)\);对于非叶子,有
计算左端点后即可二分查找右端点。
点击查看代码
//P11189
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
const ll inf = 5e18;
int c, T, n, lf[N];
vector<int> g[N];
ll a[N], b[N], le[N], ri[N];
bool chk(int x, ll v, int fa){
ll mn = 0, mx = 0;
for(int i : g[x]){
if(i == fa){
continue;
}
if(ri[i] < v){
return 0;
}
mn += max(le[i], v);
mx += ri[i];
mx = min(mx, inf);
}
ll val = b[x] + v;
return val >= mn && val <= mx;
}
void dfs(int x, int fa){
if(lf[x]){
le[x] = max(0ll, -b[x]);
ri[x] = 1e18;
return;
}
for(int i : g[x]){
if(i != fa){
dfs(i, x);
if(le[i] > ri[i]){
le[x] = 1;
ri[x] = 0;
return;
}
}
}
ll sum = 0;
for(int i : g[x]){
if(i != fa){
sum += le[i];
}
}
le[x] = max(0ll, sum - b[x]);
ll L = le[x]-1, R = 1.1e13;
while(L < R){
ll mid = L + R + 1 >> 1;
if(chk(x, mid, fa)){
L = mid;
} else {
R = mid - 1;
}
}
ri[x] = L;
}
int main(){
scanf("%d%d", &c, &T);
while(T--){
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
lf[i] = 1;
vector<int> ().swap(g[i]);
}
for(int i = 2; i <= n; ++ i){
int fa;
scanf("%d", &fa);
lf[fa] = 0;
g[fa].push_back(i);
}
for(int i = 1; i <= n; ++ i){
scanf("%lld", &a[i]);
}
for(int i = 1; i <= n; ++ i){
b[i] = a[i];
for(int j : g[i]){
b[i] -= a[j];
}
}
dfs(1, 0);
if(le[1] <= ri[1]){
puts("Huoyu");
} else {
puts("Shuiniao");
}
}
return 0;
}
2. qoj365/JOISC2017 - 鉄道旅行 (Railway Trip)
tag:倍增、正确性证明;link。
容易发现一个结论:假设一条路径 \(A=x_1,x_2,...,x_p=B\),那么一定有一条最短路满足存在 \(q\) 使得 \(\forall i\in[1,q),L_{x_i}\leq L_{x_{i+1}};\forall i\in[q,p),L_{x_i}\geq L_{x_{i+1}}\),即经过的所有点的 \(L\) 是非严格单峰的。
所以重新建图:对点 \(i\),找到它两侧离它最近的两个点 \(p,q\) 满足 \(L_p\geq L_i\leq L_q\) 并连边 \(L_i\to L_p,L_i\to L_q\)。那么原图的一条最短路可以转化为新图的 \(A\to C,B\to C\) 的两条路径。
倍增,设 \(l/r_{i,j}\) 表示 \(i\) 开始跳 \(2^j\) 步两侧到哪些点,容易证明这也意味着能到 \(L\) 最大的哪两个点。
所以询问时从 \(A\) 开始逼近 \(B\) 变成 \(A'\),然后 \(B\) 逼近 \(A'\) 得到答案,相当于先跑单峰左边的递增,再跑右边的,所以是正确的。
点击查看代码
//qoj365
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, k, q, a[N], le[N][20], ri[N][20];
int st[N], tp;
int main(){
scanf("%d%d%d", &n, &k, &q);
st[tp=0] = 1;
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
while(tp && a[st[tp]] < a[i]) -- tp;
le[i][0] = st[tp];
st[++tp] = i;
}
st[tp=0] = n;
for(int i = n; i >= 1; -- i){
while(tp && a[st[tp]] < a[i]) -- tp;
ri[i][0] = st[tp];
st[++tp] = i;
}
for(int i = 1; i <= 19; ++ i){
for(int j = 1; j <= n; ++ j){
le[j][i] = min(le[le[j][i-1]][i-1], le[ri[j][i-1]][i-1]);
ri[j][i] = max(ri[ri[j][i-1]][i-1], ri[le[j][i-1]][i-1]);
}
}
while(q--){
int x, y, ans = 0;
scanf("%d%d", &x, &y);
if(x > y) swap(x, y);
int p = x, q = x;
for(int i = 19; i >= 0; -- i){
int pp = min(le[p][i], le[q][i]);
int qq = max(ri[p][i], ri[q][i]);
if(qq < y){
p = pp;
q = qq;
ans += 1 << i;
}
}
x = q;
p = y, q = y;
for(int i = 19; i >= 0; -- i){
int pp = min(le[p][i], le[q][i]);
int qq = max(ri[p][i], ri[q][i]);
if(pp > x){
p = pp;
q = qq;
ans += 1 << i;
}
}
printf("%d\n", ans);
}
return 0;
}
3. 2023Xi'anR - Random Variables
tag:容斥;link。
设 \(F(k)\) 表示所有数出现次数 \(\leq k\) 的方案数,那么 \(m^n-F(k)\) 即为存在数出现次数 \(>k\) 的方案数,则 \(n\times m^n-\sum_{k=0}^{n-1}F(k)\) 即为答案,因为一个 \(m^n-F(k)\) 对应着所有众数出现次数 \(>k\) 的方案。根据定义一定有 \(f(0)=0\),所以只用对于每个 \(k\in[1,n)\) 求出 \(F(k)\) 即可。
考虑容斥,设 \(f_{i,j}\) 表示目前填了 \(i\) 个数,有 \(j\) 个数已经被选定次数 \(>k\)。那么转移有:
左边表示在未填的第一个位置中填入一个未被选定的数;右边表示在未填的第一个位置以及后面任意 \(k\) 个位置填一个未被选定的数并且选定它,因为多了一个选定的数所以要乘 \(-1\)。
最后答案即为 \(F(k)=\sum f_{n,i}\)。单次复杂度 \(O(\dfrac {n^2}k)\),总复杂度 \(O(n^2\log n)\)。
点击查看代码
//qoj9254
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
typedef long long ll;
int T, n;
ll m, P, f[N][N], C[N][N], ans;
struct Mod{
ll m, p;
void init(int pp){
m = ((__int128)1 << 64) / pp;
p = pp;
}
ll operator ()(ll x){
if(p == 2){
return x & 1;
}
return x - ((__int128(x) * m) >> 64) * p;
}
} mod;
ll qp(ll x, ll y){
ll ans = 1;
while(y){
if(y & 1){
ans = mod(ans * x);
}
x = mod(x * x);
y >>= 1;
}
return ans;
}
int main(){
scanf("%d%lld", &T, &P);
n = 1000;
mod.init(P);
C[0][0] = 1;
for(int i = 1; i <= n; ++ i){
C[i][0] = 1;
for(int j = 1; j <= i; ++ j){
C[i][j] = C[i-1][j] + C[i-1][j-1];
if(C[i][j] >= P){
C[i][j] -= P;
}
}
}
while(T--){
ans = 0;
scanf("%d%lld", &n, &m);
for(int k = 1; k < n; ++ k){
f[0][0] = 1;
for(int i = 1; i <= n; ++ i){
f[i][i/(k+1)+1] = 0;
for(int j = 0; j * (k + 1) <= i; ++ j){
f[i][j] = mod(f[i-1][j] * (m - j));
if(i >= k + 1 && j){
ll tmp = mod(f[i-k-1][j-1] * (m - j + 1));
tmp = mod(tmp * C[n-(i-k)][k]);
f[i][j] -= tmp;
if(f[i][j] < 0){
f[i][j] += P;
}
}
}
}
for(int j = 0; j * (k + 1) <= n; ++ j){
ans += f[n][j];
f[n][j] = 0;
if(ans >= P){
ans -= P;
}
}
}
printf("%lld\n", mod(- ans + P + n * qp(m, n)) % P);
}
return 0;
}
4. APIO2024 - Train
考虑按时间顺序 dp,设 \(f_{x,i}\) 表示 \(i\) 时刻在点 \(x\) 的最小费用,则对于每条星琼铁道有:
其中 \(w(a,b)\) 表示 \([L,R]\in (a,b)\) 的吃饭时间数,可以使用主席数求出单个的 \(w\)。
容易发现有 \(w(a,b+1)+w(a+1,b)\geq w(a,b)+w(a+1,b+1)\),即相交优于包含,可以在每个点开一个队列表示决策,二分队列决策单调性求解。
二分队列算法步骤:
- 计算队首两个决策 \(l,l+1\) 的交点是否 \(\leq i\),如果是则弹出队首;
- 若存在队首,用队首更新此时的答案;
- 计算队尾两个决策 \(r-1,r\) 的交点是否 \(\geq\) 队尾决策 \(r\) 与当前决策的交点,如果是则弹出队尾;
- 插入当前决策。
- 可以使用第二个队列维护队内交点减少算法常数。
- 使用二分求解两个决策交点,具体的,求出第一个下标使得前者劣于后者。
点击查看代码
//qoj8725
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vint;
#ifndef ONLINE_JUDGE
#include "train.h"
#endif
const int N = 1e5 + 10;
int n, m, w, pl[N*4], tp;
basic_string<int> eat[N*4], que[N], qcr[N];
basic_string<pair<int, int> > tra[N*4];
int lp[N], rp[N], mc[N];
unordered_map<int, ll> f[N];
unordered_map<int, int> vs[N];
struct SegTree{
int rt[N*4], tot = 1;
struct node{
int sum, ls, rs;
} t[N*100];
void add(int &p, int l, int r, int x){
++tot;
t[tot] = t[p];
p = tot;
if(l == r){
++ t[p].sum;
} else {
int mid = l + r >> 1;
if(x <= mid){
add(t[p].ls, l, mid, x);
} else {
add(t[p].rs, mid+1, r, x);
}
t[p].sum = t[t[p].ls].sum + t[t[p].rs].sum;
}
}
int ask(int p, int l, int r, int ql, int qr){
if(qr < l || r < ql || !p){
return 0;
} else if(ql <= l && r <= qr){
return t[p].sum;
} else {
int mid = l + r >> 1;
return ask(t[p].ls, l, mid, ql, qr) +
ask(t[p].rs, mid+1, r, ql, qr);
}
}
ll ask(int l, int r){
return ask(rt[r-1], 1, tp, l+1, tp);
}
} T;
ll fx(int x, int p, int val){
return f[x][p] + mc[x] * T.ask(p, val);
}
int crs(int x, int p, int q){
int l = q, r = tp + 1;
while(l < r){
int mid = l + r >> 1;
if(fx(x, p, mid) < fx(x, q, mid)){
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
ll solve(int N, int M, int W, vint mcc,
vint xx, vint yy, vint st, vint ed, vint lc,
vint le, vint ri){
n = N;
m = M;
w = W;
for(int i = 0; i < n; ++ i){
mc[i] = mcc[i];
lp[i] = 1;
rp[i] = 0;
que[i].push_back(0);
qcr[i].push_back(0);
}
for(int i : st){
pl[++tp] = i;
}
for(int i : ed){
pl[++tp] = i;
}
for(int i : le){
pl[++tp] = i;
}
for(int i : ri){
pl[++tp] = i;
}
sort(pl + 1, pl + tp + 1);
tp = unique(pl + 1, pl + tp + 1) - pl - 1;
for(int i = 0; i < m; ++ i){
st[i] = lower_bound(pl + 1, pl + tp + 1, st[i]) - pl;
ed[i] = lower_bound(pl + 1, pl + tp + 1, ed[i]) - pl;
tra[st[i]].push_back(make_pair(-i-1, xx[i]));
tra[ed[i]].push_back(make_pair(i+1, yy[i]));
}
for(int i = 0; i < w; ++ i){
le[i] = lower_bound(pl + 1, pl + tp + 1, le[i]) - pl;
ri[i] = lower_bound(pl + 1, pl + tp + 1, ri[i]) - pl;
eat[ri[i]].push_back(le[i]);
}
for(int i = 1; i <= tp; ++ i){
T.rt[i] = T.rt[i-1];
for(int j : eat[i]){
T.add(T.rt[i], 1, tp, j);
}
}
f[0][0] = 0;
que[0].push_back(0);
qcr[0].push_back(0);
que[0][++rp[0]] = 0;
for(int i = 1; i <= tp; ++ i){
sort(tra[i].begin(), tra[i].end());
for(auto j : tra[i]){
int id = j.first, pos = j.second;
if(id < 0){
id = -id - 1;
ll val = 1e18;
while(lp[pos] < rp[pos] && qcr[pos][lp[pos]] <= i){
++ lp[pos];
}
if(lp[pos] > rp[pos]){
continue;
}
int p = que[pos][lp[pos]];
val = f[pos][p] + mc[pos] * T.ask(p, i);
if(f[pos].find(i) != f[pos].end()){
f[pos][i] = min(f[pos][i], val);
} else {
f[pos][i] = val;
}
} else {
-- id;
if(f[xx[id]].find(st[id]) == f[xx[id]].end()){
continue;
}
ll val = f[xx[id]][st[id]] + lc[id];
if(f[pos].find(i) != f[pos].end()){
f[pos][i] = min(f[pos][i], val);
} else {
f[pos][i] = val;
}
while(lp[pos] < rp[pos] &&
qcr[pos][rp[pos]-1] >=
crs(pos, que[pos][rp[pos]], i)){
-- rp[pos];
}
que[pos].push_back(0);
qcr[pos].push_back(0);
que[pos][++rp[pos]] = i;
if(lp[pos] < rp[pos]){
qcr[pos][rp[pos]-1] = crs(pos, que[pos][rp[pos]-1], que[pos][rp[pos]]);
}
}
}
}
ll val = 1e18;
for(auto i : f[n-1]){
val = min(val, i.second + 1ll * mc[n-1] * T.ask(i.first, tp+1));
}
if(val > 1e17){
return -1;
}
return val;
}

浙公网安备 33010602011771号