2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛 L 捡贝壳 && 你能回答这些问题吗

  一道类似捡贝壳的简化版本,这个题写了再写捡贝壳比较好
  你能回答这些问题吗

  很明显这种区间问题会用到线段树,然后首先考虑如何维护区间最大连续子序列,我们会发现对于线段树上每个非叶子结点(有儿子的结点)最大子序列无非就三种可能,一种是被左儿子完全覆盖,另一种是被右儿子完全覆盖,最后一种就是左儿子从左儿子的最右边往左走的的最大值(rmx)加上右儿子从右儿子的最左边往右走的最大值(lmx)。那么如何维护左右儿子的rmx和lmx还有左右儿子的最大连续子序列值(mx)呢?

  我们会发现在维护当前结点的lmx和rmx的时候有两个可能,当前节点的lmx的一个可能是左子树的lmx,另一个可能是左子树的总和加上右子树的lmx,我们需要取最大值。当前节点的rmx同理,这么一来我就需要再维护一个区间累加和。

  我们线段树上维护四个信息,从左端点向右连续的最大值lmx,从右端点向左连续的最大值rmx,区间最大值mx,区间和sum,在叶子结点的时候rmx=lmx=mx=sum=叶子对应的值,每次pushup的时候如何维护四个信息?

  对于lmx更新的时候需要用左儿子的lmx和左儿子的和加上右儿子的lmx取max

  对于rmx更新的时候需要用右儿子的rmx和右儿子的和加上左儿子的rmx取max

  对于sum直接左右儿子加起来即可

  对于区间最值,在左儿子的mx右儿子的mx左儿子的rmx加上右儿子的lmx取max即可

  代码实现如下

#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <tuple>
#include <array>
 
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define cntone(x) __builtin_popcount(x)
#define db double
#define fs first
#define se second
#define AC main(void)
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
typedef std::pair<int, int > PII;
typedef std::pair<int, std::pair<int, int>> PIII;
typedef std::pair<ll, ll> Pll;
typedef std::pair<double, double> PDD;
using ld = double long;

const long double eps = 1e-9;
const int INF = 0x3f3f3f3f;
const int N = 5e5 + 10, M = 4e5 + 10;
int n , m, p;

int d1[] = {0, 0, 1, -1};
int d2[] = {1, -1, 0, 0};

int a[N];

struct node{
	int l, r;
	ll lmx, rmx, sum, mx;
}tr[N << 2];

inline void pushup(node &u,node &l,node &r){
    u.sum = l.sum + r.sum;
    u.lmx = std::max(l.lmx, l.sum + r.lmx);
    u.rmx = std::max(r.rmx, r.sum + l.rmx);
    u.mx = std::max({l.mx, r.mx, l.rmx + r.lmx});
}

inline void pushup(int u){
	pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

inline void build_tree(int u, int l, int r){
	tr[u] = {l, r};
	if(l == r){
		tr[u].lmx = tr[u].rmx = tr[u].mx = tr[u].sum = a[l];
		return ;
	}
	
	int mid = l + r >> 1;
	build_tree(u << 1, l, mid);
	build_tree(u << 1 | 1, mid + 1, r);
	pushup(u);
}

inline void modify(int u, int x, int v){
	if(tr[u].l == tr[u].r){
		tr[u].sum = tr[u].lmx = tr[u].rmx = tr[u].mx = v;
		return ;
	}
	
	int mid = tr[u].l + tr[u].r >> 1;
	if(x <= mid)	modify(u << 1, x, v);
	else modify(u << 1 | 1, x, v);
	pushup(u);
}

inline node query(int u, int l, int r){
	if(tr[u].l >= l && tr[u].r <= r)	return tr[u];
	int mid = tr[u].l + tr[u].r >> 1;
	if(r <= mid)	return query(u << 1, l, r);
	if(l > mid)	return query(u << 1 | 1, l ,r);
	node res;
	node ls = query(u << 1, l, r);
	node rs = query(u << 1 | 1, l, r);
	pushup(res, ls, rs);	

	return res;
}

inline void solve(){
	std::cin >> n >> m;
	for(int i = 1; i <= n; i ++)	std::cin >> a[i];
	build_tree(1, 1, n);
	while(m --){
		int op, l, r;
		std::cin >> op >> l >> r;
		if(op == 1){
			if(l > r)	std::swap(l, r);
			std::cout << query(1, l, r).mx << '\n';
		}	
		else modify(1, l, r);
	}
}

signed AC{
   	HYS
   	
	int _ = 1;
	//std::cin >> _;
	while(_ --)
        solve();

    return 0;
}

取贝壳

题目链接

这题思路和上题类似,只不过我们需要把维护一个lmx,rmx,mx,sum变成维护16个lmx,rmx,mx和一个区间长度。lazy是懒标记下传,state每个结点里面有哪些贝壳。
因为最多有四种贝壳,所以我们可以考虑状态压缩把四种贝壳表示为1——15之间的整数,然后区间状态合并变成区间或。
因为n是3e5所以进行pushup的时候用两个for循环枚举所有状态进行暴力转移即可。

需要注意的细节

用i枚举左儿子状态和j枚举右儿子状态的时候,如果i与上左儿子的state不等于i的话就说明当前的i这个状态是不能被左儿子完全包含的所以就不能用左儿子的len+右儿子的lmx来更新当前节点的lmx,当前结点的rmx也同理。

#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <tuple>
#include <array>
 
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define cntone(x) __builtin_popcount(x)
#define db double
#define fs first
#define se second
#define AC main(void)
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
typedef std::pair<int, int > PII;
typedef std::pair<int, std::pair<int, int>> PIII;
typedef std::pair<ll, ll> Pll;
typedef std::pair<double, double> PDD;
using ld = double long;

const long double eps = 1e-9;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10, M = 4e5 + 10;
int n , m, p;

int d1[] = {0, 0, 1, -1};
int d2[] = {1, -1, 0, 0};

int a[N];

struct node{
	int l, r;
	int state, lazy;
	int lmn[16], rmn[16], mn[16], len;
}tr[N << 2];

int target = 15;

inline void pushup(node &u,node &ls,node &rs){
    u.len = ls.len + rs.len;
    u.state = ls.state | rs.state;
    for(int i = 1; i < 16; i ++){
    	u.lmn[i] = ls.lmn[i];
    	u.rmn[i] = rs.rmn[i];
    	u.mn[i] = std::min(ls.mn[i], rs.mn[i]);
    }
    for(int i = 1; i < 16; i ++){
    	for(int j = 1; j < 16; j ++){
    		int tmp = i | j;
    		if((ls.state & i) == i)    u.lmn[tmp] = std::min(u.lmn[tmp], ls.len + rs.lmn[j]);
    		if((rs.state & j) == j)    u.rmn[tmp] = std::min(u.rmn[tmp], rs.len + ls.rmn[i]);
    		u.mn[tmp] = std::min({u.mn[tmp], ls.rmn[i] + rs.lmn[j]});
    	}
    }
}

inline void pushup(int u){
	pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

inline void Do(int u, int v){
    memset(tr[u].lmn, 0x3f, sizeof tr[u].lmn);
    memset(tr[u].rmn, 0x3f, sizeof tr[u].rmn);
    memset(tr[u].mn, 0x3f, sizeof tr[u].mn);
    tr[u].state = v;
    tr[u].lmn[v] = tr[u].mn[v] = tr[u].rmn[v] = 1;
    tr[u].lazy = v;
}

inline void pushdown(int u){
	if(tr[u].lazy){
		Do(u << 1, tr[u].lazy);
		Do(u << 1 | 1, tr[u].lazy);
		tr[u].lazy = 0;
	}
}

inline void build_tree(int u, int l, int r){
	tr[u] = {l, r};
	if(l == r){
		int t = 1 << (a[l] - 1);
		tr[u].state = t;
                memset(tr[u].lmn, 0x3f, sizeof tr[u].lmn);
                memset(tr[u].rmn, 0x3f, sizeof tr[u].rmn);
                memset(tr[u].mn, 0x3f, sizeof tr[u].mn);
		tr[u].lmn[t] = tr[u].rmn[t] = tr[u].len = tr[u].mn[t] = 1; 
		return ;
	}
	
	int mid = l + r >> 1;
	build_tree(u << 1, l, mid);
	build_tree(u << 1 | 1, mid + 1, r);
	pushup(u);
}

inline void modify(int u, int l, int r, int v){
	if(tr[u].l >= l && tr[u].r <= r){
		Do(u, v);
		return ;
	}
	pushdown(u);
	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid)	modify(u << 1, l, r, v);
	if(r > mid)	modify(u << 1 | 1, l, r, v);
	pushup(u);
}

inline node query(int u, int l, int r){
	if(tr[u].l >= l && tr[u].r <= r)	return tr[u];
	pushdown(u);
	int mid = tr[u].l + tr[u].r >> 1;
	if(r <= mid)	return query(u << 1, l, r);
	if(l > mid)	return query(u << 1 | 1, l, r);
	
	node res;
	node ls = query(u << 1, l, r);
	node rs = query(u << 1 | 1, l, r);
	pushup(res, ls, rs);
	pushup(u);
	return res;
}

inline void solve(){
	std::cin >> n;
	for(int i = 1; i <= n; i ++)	std::cin >> a[i];
	build_tree(1, 1, n);
	std::cin >> m;
	for(int i = 1; i <= m; i ++){
		int op, l, r, v;
		std::cin >> op >> l >> r;
		if(op == 1){
			std::cin >> v;
			v = 1 << (v - 1);
			modify(1, l, r, v);
		}
		else{
			int tmp = query(1, l, r).mn[15];
			if(tmp != INF)	std::cout << tmp << '\n';
			else std::cout << -1 << '\n';
		} 
	}
}

signed AC{
   	HYS
   	
	int _ = 1;
	//std::cin >> _;
	while(_ --)
        solve();

    return 0;
}
posted @ 2023-03-17 11:24  春始于雪之下  阅读(40)  评论(0编辑  收藏  举报