首页 > 其他分享 >【YBT2023寒假Day12 B】仰望星空(DP)(线段树)(笛卡尔树)

【YBT2023寒假Day12 B】仰望星空(DP)(线段树)(笛卡尔树)

时间:2023-02-22 10:11:56浏览次数:45  
标签:子树 return 笛卡尔 int 最大值 YBT2023 Day12 ll DP

仰望星空

题目链接:YBT2023寒假Day12 B

题目大意

有一个 n*n 的网格,第 i 列下面的 ai 个点都是障碍。
然后又一些不是障碍的地方有特殊点,删掉它有费用。
要你用最小费用使得不存在两个特殊点在一个矩阵中且矩阵中没有障碍。

思路

注意到障碍很特殊,考虑从这里开始思考。
会发现两个特殊点可以同时存在的条件是它们所在的列形成的区间 \(a_i\) 的最大值要大于行小的那个。

那你可以考虑保留那些点,使得每对都是这样的,而且保留下的费用最大。
那这个最大值不难想到笛卡尔树。
考虑对于笛卡尔树上的一个点,它的区间是 \(l\sim r\),那个最大值是 \(x\),它父亲的最大值是 \(y\)(如果没有父亲那 \(y=N\))。
那对于 \([l\sim r][x+1,y]\) 是一个空的,这里可以放一个点。

那考虑放或者不放。
不放的话,那问题就直接变成两个子树的答案和。
放的话,那如果它放在了第 \(i\) 列,那我们一直到属于第 \(i\) 列的最底下的点,路径上的所有点都不能放了。
那我们就要把剩下的子树的 DP 值加起来得到答案。

那至于怎么加,我们可以发现要加的子树是这条链上每个点的另一个兄弟的答案。
那我们除了 \(f_x\) 表示 \(i\) 子树的答案,再设一个 \(f'_x\) 是它兄弟的答案,那我们用一个线段树,每次给 \(x\) 代表的区间加上贡献,询问就是单点查询了(所以树状数组也行)。

代码

#include<set>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

const int N = 2e5 + 100; 	
struct node {
	int x, y, c;
}b[N];
int n, a[N], m, id[N];
set <int> s;
ll ans;

bool cmpa(int x, int y) {
	return a[x] < a[y];
}

bool cmpb(node x, node y) {
	if (x.y != y.y) return x.y < y.y;
	return x.x < y.x;
}

struct XD_tree {
	ll f[N << 2];
	
	void update(int now, int l, int r, int L, int R, ll x) {
		if (L > R) return ;
		if (L <= l && r <= R) {
			f[now] += x; return ;
		}
		int mid = (l + r) >> 1;
		if (L <= mid) update(now << 1, l, mid, L, R, x);
		if (mid < R) update(now << 1 | 1, mid + 1, r, L, R, x);
	}
	
	ll query(int now, int l, int r, int pl) {
		if (l == r) return f[now];
		int mid = (l + r) >> 1;
		if (pl <= mid) return f[now] + query(now << 1, l, mid, pl);
			else return f[now] + query(now << 1 | 1, mid + 1, r, pl);
	}
}T;

int main() {
	freopen("starry.in", "r", stdin);
	freopen("starry.out", "w", stdout);
	
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]), id[i] = i;
	sort(id + 1, id + n + 1, cmpa);
	scanf("%d", &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].c);
	}
	sort(b + 1, b + m + 1, cmpb);
	
	for (int i = 0; i <= n + 1; i++) s.insert(i);
	int L = 1;
	for (int i = 1; i <= m; i++) {
		while (L <= n && a[id[L]] < b[i].y) s.erase(s.find(id[L])), L++;
		ll no = b[i].c, yes = T.query(1, 1, n, b[i].x);
		if (no < yes) {
			ans += no;
		}
		else {
			ans += yes;
			set <int> ::iterator it = s.lower_bound(b[i].x);
			int r = (*it) - 1, l = (*(--it)) + 1;
			T.update(1, 1, n, l, r, -yes + b[i].c);//撤销选的操作并补回不选的费用
		}
		
	}
	printf("%lld", ans);
	
	return 0;
}

标签:子树,return,笛卡尔,int,最大值,YBT2023,Day12,ll,DP
From: https://www.cnblogs.com/Sakura-TJH/p/YBT2023Day12_B.html

相关文章

  • 【YBT2023寒假Day12 A】我的世界(二分)(主席树)
    我的世界题目链接:YBT2023寒假Day12A题目大意有n个数,每一秒每个数都会减小1,而且你可以选一个数让它减小x,小于0的数会变成0。给你s秒,问你s秒操作后所有数中......
  • 内核通用xdp
    目前想做个功能,一些简单转发就在内核中直接转发走,所以想到了XDP,但是呢,部分网卡驱动不支持XDP程序,能不能在netif_recv收包接口或者驱动的hook钩子挂载一个通用接口来处理XD......
  • udp通信
    服务端:importsocketsk=socket.socket(type=socket.SOCK_DGRAM)sk.bind(("127.0.0.1",8080))whileTrue:msg,addr=sk.recvfrom(1024)print(msg.dec......
  • 【YBT2023寒假Day11 B】催眠大师(费用流)
    催眠大师题目链接:YBT2023寒假Day11B题目大意有一个n*n的棋盘,有一些位置有障碍。然后定义棋盘上两个位置能相互攻击当且仅当在同一行或同一列,且之间的所有位置都没有......
  • POJ 1088 滑雪 递归+dp | 拓扑排序
    从每个点(i,j)向四个方向去看如果某一个方向(a,b)的数值比当前位置小先求解(a,b)的最长距离,之后加1即可朴素的递归重复求解了很多子问题,我们每计算出一个子问题的解,便......
  • POJ 2228 Naptime 环形DP
    先不考虑环的情况dp[i][j][0]:前i个时间间隔中,已经花费了j个间隔,取得的最大值,并且第i个间隔在休息dp[i][j][1]:前i个时间间隔中,已经花费了j个间隔,取得的最大值,并且第i个间......
  • P2602 [ZJOI2010] 数字计数:数位DP
    https://www.luogu.com.cn/problem/P2602//#include<iostream>//#include<iomanip>//#include<unistd.h>//#include<climits>//#include<string>//#inclu......
  • TCP与UDP简述
    什么是TCPTCP(TransmissionControlProtocol传输控制协议)是一种面向连接的,可靠的,基于字节流的传输通信协议。1、tcp(TransmissionControlProtocol传输控制协议)2、传......
  • BZOJ 4145 [AMPPZ2014]The Prices (状压DP)
    题意你要购买商店,你到第i家商店的路费为,在第家商店购买第种物品的费用为,求最小总费用。分析很容易定义出状态,表示到第行,买的物品情况为的最小费用。按照往常的套路是转移时......
  • [oeasy]python0089_大型机的衰落_Dec小型机崛起_PDP_VAX网络
    编码进化回忆上次内容上次回顾了计算机存储单位的演变最小的读写单位是bit8-bit固定下来成为了字节(Byte)位数容量8-bit1Byte1024Byte......