P8818
线段树/ST表
当小L想要在A中选一个正数,判断一遍B中的最小值(因为小Q会选一个最小的数来让结果最小)。如果Bmin > 0,那么小L会选一个最大的正数。如果如果Bmin < 0,那么小L会选一个最小的正数
当小L想要在A中选一个负数,判断一遍B中的最大值(因为小Q会选一个最大的数来让结果最小)。如果Bmax > 0,那么小L会选一个最小的负数。如果如果Bmax < 0,那么小L会选一个最大的负数
特殊的考虑 0 带入会发现 可以随便划分,结果正确人为的都划分到上半部分,情况可以分 4 类去讨论
可以维护6个线段树/ST表,分别维护:
(0) 求A的某个区间的最小的非负整数
(1) 求A的某个区间的最大的非负整数
(2) 求A的某个区间的最小的负数
(3) 求A的某个区间的最大的负数
(4) 求B的某个区间的最小值
(5) 求B的某个区间的最大值
//线段树
#include<bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 1e5 + 5, INF = 2e9;
int n, m, Q;
int a[N], b[N];
struct node{
int l, r, v;
}tr[6][N << 2];
int get(int u, int type){
if(type == 0) return a[u] >= 0 ? a[u] : INF;
if(type == 1) return a[u] >= 0 ? a[u] : -INF;
if(type == 2) return a[u] < 0 ? a[u] : INF;
if(type == 3) return a[u] < 0 ? a[u] : -INF;
return b[u];
}
void pushup(node tr[], int u, int type){
int left = tr[ls].v, right = tr[rs].v;
if(type % 2 == 0) tr[u].v = min(left, right);
else tr[u].v = max(left, right);
}
void build(node tr[], int u, int l, int r, int type){
tr[u] = {l, r};
if(l == r)
tr[u].v = get(r, type);
else{
int mid = l + r >> 1;
build(tr, ls, l, mid, type);
build(tr, rs, mid + 1, r, type);
pushup(tr, u, type);
}
}
int query(node tr[], int u, int l, int r, int type){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].v;
int mid = tr[u].l + tr[u].r >> 1;
if(type % 2 == 0){
int res = INF;
if(l <= mid) res = min(res, query(tr, ls, l, r, type));
if(r > mid) res = min(res, query(tr, rs, l, r, type));
return res;
}
else{
int res = -INF;
if(l <= mid) res = max(res, query(tr, ls, l, r, type));
if(r > mid) res = max(res, query(tr, rs, l, r, type));
return res;
}
}
int main(){
cin >> n >> m >> Q;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= m; i++) cin >> b[i];
for(int i = 0; i < 4; i++) build(tr[i], 1, 1, n, i);
for(int i = 4; i < 6; i++) build(tr[i], 1, 1, m, i);
while(Q--){
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
int ymin = query(tr[4], 1, l2, r2, 4);
int ymax = query(tr[5], 1, l2, r2, 5);
long long x, res = -1e18;
if(ymin >= 0) x = query(tr[1], 1, l1, r1, 1);
else x = query(tr[0], 1, l1, r1, 0);
if(abs(x) != INF) res = max(res, x * ymin);
if(ymax >= 0) x = query(tr[3], 1, l1, r1, 3);
else x = query(tr[2], 1, l1, r1, 2);
if(abs(x) != INF) res = max(res, x * ymax);
cout << res << endl;
}
return 0;
}
//ST表
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 0x7f7f7f7f;
const int N = 1e5 + 5;
int n, m, q, a[N], b[N];
int lg[N], amax[N][30], amin[N][30], aminn[N][30], amaxx[N][30], bmax[N][30], bmin[N][30];
void init() {
for (int i = 1; i <= n; i ++) amax[i][0] = amin[i][0] = a[i];
for (int j = 1; j <= 25; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++)
amax[i][j] = max(amax[i][j - 1], amax[i + (1 << (j - 1))][j - 1]),
amin[i][j] = min(amin[i][j - 1], amin[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= n; i ++){
amaxx[i][0] = a[i] >= 0 ? -inf : a[i];
aminn[i][0] = a[i] < 0 ? inf : a[i];
}
for (int j = 1; j <= 25; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++)
amaxx[i][j] = max(amaxx[i][j - 1], amaxx[i + (1 << (j - 1))][j - 1]),
aminn[i][j] = min(aminn[i][j - 1], aminn[i + (1 << (j - 1))][j - 1]);
for (int i = 1; i <= m; i ++) bmax[i][0] = bmin[i][0] = b[i];
for (int j = 1; j <= 25; j ++)
for (int i = 1; i + (1 << j) - 1 <= m; i ++)
bmax[i][j] = max(bmax[i][j - 1], bmax[i + (1 << (j - 1))][j - 1]),
bmin[i][j] = min(bmin[i][j - 1], bmin[i + (1 << (j - 1))][j - 1]);
}
int query(int l1, int r1, int l2, int r2) {
int len1 = log2(r1 - l1 + 1), len2 = log2(r2 - l2 + 1);
int amx = max(amax[l1][len1], amax[r1 - (1 << len1) + 1][len1]);
int amn = min(amin[l1][len1], amin[r1 - (1 << len1) + 1][len1]);
int amxx = max(amaxx[l1][len1], amaxx[r1 - (1 << len1) + 1][len1]);
int amnn = min(aminn[l1][len1], aminn[r1 - (1 << len1) + 1][len1]);
int bmx = max(bmax[l2][len2], bmax[r2 - (1 << len2) + 1][len2]);
int bmn = min(bmin[l2][len2], bmin[r2 - (1 << len2) + 1][len2]);
//a一定会选让结果更大的一个,那么取一下max
int res = 1ll * -inf * inf;
if (amx >= 0 && bmn >= 0) res = max(res, 1ll * amx * bmn);
if (amx >= 0 && bmn < 0 && amnn != inf) res = max(res, 1ll * amnn * bmn);
if (amn < 0 && bmx >= 0 && amxx != -inf) res = max(res, 1ll * amxx * bmx);
if (amn < 0 && bmx < 0) res = max(res, 1ll * amn * bmx);
return res;
}
signed main() {
cin >> r >> m >> q;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= m; i ++) cin >> b[i];
init();
while (q--) {
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
cout << query(l1, r1, l2, r2) << endl;
}
return 0;
}
P2024
并查集
p数组维护这个点的祖先,d数组维护在这个点上面食物链的长度。
t = 1时,“if(px == py && (d[x] - d[y]) % 3)”说明它们两个在同一条食物链上且他们的距离不为3的倍数(不是同类),答案++,否则是真话,将它们两个联系到一起。
t = 2时,“if(px == py && (d[x] - d[y] - 1) % 3)”说明它们两个在同一条食物链上且距离为3的倍数(是同类),答案++,否则是真话,将它们两个联系到一起。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int p[N], d[N];
int t, x, y;
int ans, n, m;
int find(int x){
if(x != p[x]){
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) p[i] = i;
while(m--){
cin >> t >> x >> y;
if(x > n || y > n) ans++;
else{
int px = find(x), py = find(y);
if(t == 1){
if(px == py && (d[x] - d[y]) % 3) ans++;
else if(px != py){
p[px] = py;
d[px] = d[y] - d[x];
}
}else{
if(px == py && (d[x] - d[y] - 1) % 3) ans++;
else if(px != py){
p[px] = py;
d[px] = d[y] - d[x] + 1;
}
}
}
}
cout << ans;
return 0;
}
数位DP的讲解
度的数量
数位DP/记忆化搜索
把题目转换为:
把当前数字转换为B进制下,有K个1,其余都是0。
从1~r、1~l-1搜索有k个1的数,pos表示当前位置,st表示现在有几个1,limit表示是否为最大值(贴合上界)将每个情况搜索出来。
#include<bits/stdc++.h>
using namespace std;
const int N = 40;
int l, r, k, b, idx, ans;
int f[N][N], a[N];
int dfs(int pos, int st, int limit){
if(pos == 0) return st == k;
if(!limit && f[pos][st] != -1) return f[pos][st];
int res = 0, up = limit ? min(a[pos], 1) : 1;
for(int i = 0; i <= up; i++){
if(st + i > k) continue;
res += dfs(pos - 1, st + i, limit && i == a[pos]);
}
return limit ? res : f[pos][st] = res;
}
int calc(int x){
memset(f, -1, sizeof f);
idx = 0;
while(x){
a[++idx] = x % b;
x /= b;
}
return dfs(idx, 0, 1);
}
int main(){
cin >> l >> r >> k >> b;
cout << calc(r) - calc(l - 1);
return 0;
}
数字游戏
数位DP/记忆化搜索
pos表示当前数位,pre表示上一位的数,limit表示是否贴合上界,搜索每一位数,统计答案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 35;
int l, r;
int a[N], idx;
int f[N][N];
int dfs(int pos, int pre, int limit){
if(pos == 0) return 1;
if(!limit && f[pos][pre] != -1) return f[pos][pre];
int ans = 0, up = limit ? a[pos] : 9;
for(int i = pre; i <= up; i++){
ans += dfs(pos - 1, i, limit && i == up);
}
return limit ? ans : f[pos][pre] = ans;
}
int calc(int x){
idx = 0;
memset(f, -1, sizeof f);
while(x){
a[++idx] = x % 10;
x /= 10;
}
return dfs(idx, 0, 1);
}
signed main(){
while(cin >> l >> r){
cout << calc(r) - calc(l - 1) << endl;
}
return 0;
}
P2657
数位DP/记忆化搜索
pos表示当前位置,pre表示上一位的数,lead表示是否含有前导零,limit表示是否贴合上界。
“abs(i - pre) < 2”为题目描述中对windy数反过来的定义
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 35;
int l, r;
int a[N], idx;
int f[N][N];
int dfs(int pos,int pre,int lead,int limit){
if(pos == 0) return 1;
if(!limit && !lead && f[pos][pre] != -1) return f[pos][pre];
int res = 0,up = limit ? a[pos] : 9;
for(int i = 0;i <= up;i ++){
if(!lead && abs(i - pre) < 2) continue;
res += dfs(pos - 1,i,lead && i == 0,limit && i == up);
}
if(!limit && !lead) f[pos][pre] = res;
return res;
}
int calc(int x){
idx = 0;
memset(f, -1, sizeof f);
while(x){
a[++idx] = x % 10;
x /= 10;
}
return dfs(idx, 0, 1, 1);
}
signed main(){
cin >> l >> r;
cout << calc(r) - calc(l - 1) << endl;
return 0;
}
数位DP模板伪代码
int dfs(int pos, int pre, int lead, int limit) {
if (!pos) {
边界条件
}
if (!limit && !lead && dp[pos][pre] != -1) return dp[pos][pre];
int res = 0, up = limit ? a[pos] : 无限制位;
for (int i = 0; i <= up; i ++) {
if (不合法条件) continue;
res += dfs(pos - 1, 未定参数, lead && !i, limit && i == up);
}
return limit ? res : (lead ? res : dp[pos][sum] = res);
}
int cal(int x) {
memset(dp, -1, sizeof dp);
len = 0;
while (x) a[++ len] = x % 进制, x /= 进制;
return dfs(len, 未定参数, 1, 1);
}
signed main() {
cin >> l >> r;
cout << cal(r) - cal(l - 1) << endl;
}

902

被折叠的 条评论
为什么被折叠?



