【ARC085F】NRE

题目描述

一个全部为 0 的数组 a 。给01数组 bq 个操作,每个操作 li,ri ,即将 a[li,ri] 全部赋值为 1 ,你可以选一些操作,不要一些操作,求最后 ab 最少有多少位不同。

1n,q2×105

算法解析

很多区间,求最值,不可贪心,一眼 dp。

考虑一个状态数为 q 的 dp,设 fi 是考虑前 i 个区间,且第 i 个必选,[1,ri] 的答案。这种区间会相互影响的题,一般思路是考虑区间的位置关系:

如果不交:

fi=minj<ifj+num1[rj+1,li1]+num0[li,ri]

如果相交:

fi=minj<ifj+num0[rj+1,ri]

分为这两种情况,由于 num00 个数)和 num11 个数)可以差分,我们考虑将 j 的信息统一放进两棵线段树里面,取最小值。每次单点修改,区间查询。

但是笔者在这样实现的时候就傻眼了:第 5 个样例中有完全包含的情况,这种情况不应该用来转移,也就是说 j 的左右端点都有要求,这时候我们怎么办呢?树套树或者 CDQ?

事实上,我们尝试转变线段树的模式,每个 fi 算出来之后分别贡献到 [li,ri][ri+1,n] ,然后查询的时候单点查询当前区间左端点处的答案即可,就通过只贡献一部分的形式解决了这个问题。

这启发我们线段树的模式不同,最后求答案的难易度也不同,写线段树时要向模板题那样明确你要什么操作。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5,inf = 0x3f3f3f3f;
struct Segment_Tree{
	int a[N << 2],tag[N << 2];
	inline void pushdown(int pos)
	{
		a[pos << 1] = min(a[pos << 1],tag[pos]);
		a[pos << 1 | 1] = min(a[pos << 1 | 1],tag[pos]);
		tag[pos << 1] = min(tag[pos << 1],tag[pos]);
		tag[pos << 1 | 1] = min(tag[pos << 1 | 1],tag[pos]);
		tag[pos] = inf;
	}
	inline void pushup(int pos) {a[pos] = min(a[pos << 1],a[pos << 1 | 1]);}
	inline void modify(int l,int r,int L,int R,int k,int pos)
	{
		if(L <= l && r <= R) {a[pos] = min(a[pos],k); tag[pos] = min(tag[pos],k); return;}
		int mid = (l + r) >> 1;
		pushdown(pos);
		if(L <= mid) modify(l,mid,L,R,k,pos << 1);
		if(R > mid) modify(mid + 1,r,L,R,k,pos << 1 | 1);
		pushup(pos);
	}
	inline int query(int l,int r,int x,int pos)
	{
		if(l == r) return a[pos];
		int mid = (l + r) >> 1,ret = inf;
		pushdown(pos);
		if(x <= mid) ret = query(l,mid,x,pos << 1);
		else ret = query(mid + 1,r,x,pos << 1 | 1);
		pushup(pos);
		return ret;
	}
}t1,t2;
int f[N],n,b[N],m,num0[N],num1[N];
struct Q{
	int l,r;
}q[N];
int main()
{
	memset(t1.a,0x3f,sizeof(t1.a)); memset(t2.a,0x3f,sizeof(t2.a)); memset(t1.tag,0x3f,sizeof(t1.tag)); memset(t2.tag,0x3f,sizeof(t2.tag));
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%d",&b[i]);
	for(int i = 1;i <= n;i++) num0[i] = num0[i - 1] + (b[i] == 0),num1[i] = num1[i - 1] + (b[i] == 1);
	scanf("%d",&m);
	for(int i = 1;i <= m;i++) scanf("%d%d",&q[i].l,&q[i].r);
	sort(q + 1,q + m + 1,[&](Q x,Q y) {if(x.r != y.r) return x.r < y.r; return x.l > y.l;});
	t1.modify(0,n,0,n,0,1); q[0].r = 0;
	for(int i = 1;i <= m;i++)
	{
		int val1 = t1.query(0,n,q[i].l,1) + num1[q[i].l - 1] + num0[q[i].r] - num0[q[i].l - 1];
		int val2 = t2.query(0,n,q[i].l,1) + num0[q[i].r];
		f[i] = min(val1,val2);
		t1.modify(0,n,q[i].r + 1,n,f[i] - num1[q[i].r],1);
		t2.modify(0,n,q[i].l,q[i].r,f[i] - num0[q[i].r],1);
	}
	int ans = inf;
	for(int i = 0;i <= m;i++) ans = min(ans,f[i] + num1[n] - num1[q[i].r]);
	printf("%d",ans);
	return 0;
} 
posted @   The_Last_Candy  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示