2025.02.03

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值