ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P8441 旭日东升(二维数点经典套路)

2022-08-14 17:31:00  阅读:177  来源: 互联网

标签:opt le P8441 val int 数点 旭日东升 区间 emplace


P8441 旭日东升

维护一个不可重集合的序列 \(a\),长度为 \(n\)。支持以下两种操作:

  • l r x 对于每个 \(l\le i\le r\),将 \(x\) 并入 \(a_i\)。
  • l r 设 \(S\) 把每个 \(l\le i\le r\) 的 \(a_i\) 并在一起的集合,输出 \(S\) 中所有元素的和。

\(n,m,x\le 10^5,1\le l\le r\le n\)。

\(\color{yellow}{\bigstar\texttt{Trick}}\):连续的不可重集合 \(\rightarrow\) 含有每种颜色的地方以不重叠的区间形式存在,考虑用 ODT 维护区间中是否有这个数。如果我们能在每次操作之后,都将 \([1,n]\) 的区间划分不存在连续的 \(0\) 区间的若干段,那么不含有这个数的区间一定是被完全包含在 \(0\) 区间中

为了实现对区间端点的限制,两位神仙为我们指引了方向!!

\(\texttt{p}\color{red}{\texttt{igstd}}\):这个问题可以转化为矩形加、单点求值,之后想方法实现。

\(\texttt{B}\color{red}{\texttt{uttercake}}\):矩形加、单点求值可以将询问和修改离线按照左端点排序,将操作存储在左端点并用树状数组维护,但常数很小。(update:即将矩形差分后离线处理)

于是你就做完了!!!

此题代码
#define Maxn 100005
int n,m;
ll ans[Maxn];
struct BIT
{
	ll tree[Maxn];
	inline void add(int x,ll k){ while(x<=n) tree[x]+=k,x+=x&(-x); }
	inline ll query(int x){ ll ret=0; while(x) ret+=tree[x],x-=x&(-x); return ret; }
}T;
struct ODT
{
	int l,r,val;
	ODT(int L=0,int R=0,int Val=0):l(L),r(R),val(Val){}
	bool friend operator < (ODT x,ODT y) { return x.l<y.l; }
};
set<ODT> s[Maxn];
struct Operation
{
	int opt,l,r,val,num;
	Operation(int Opt=0,int L=0,int R=0,int Val=0,int Num=0):
		opt(Opt),l(L),r(R),val(Val),num(Num){}
	bool friend operator < (Operation x,Operation y) { return x.l<y.l; }
};
vector<Operation> op;
inline void add(int l,int r,int val)
{
	op.emplace_back(0,l,l,val,0);
	op.emplace_back(0,l,r+1,-val,0);
	op.emplace_back(0,r+1,l,-val,0);
	op.emplace_back(0,r+1,r+1,val,0);
}
inline auto split(int opt,int x)
{
	if(!s[opt].size()) s[opt].emplace(1,n+1,0);
	auto it=s[opt].lower_bound(ODT(x,0,0));
	if(it!=s[opt].end() && it->l==x) return it;
	it--;
	int l=it->l,r=it->r,val=it->val;
	if(it->val==0) add(l,r,opt),add(l,x-1,-opt),add(x,r,-opt);
	s[opt].erase(it);
	s[opt].emplace(l,x-1,val);
	return s[opt].emplace(x,r,val).fi;
}
inline void assign(int opt,int l,int r)
{
	if(!s[opt].size()) s[opt].emplace(1,n+1,0);
	auto itr=split(opt,r+1),itl=split(opt,l);
	for(auto it=itl;it!=itr;it++)
		if(it->val==0) add(it->l,it->r,opt);
	s[opt].erase(itl,itr);
	s[opt].emplace(l,r,1);
}
void solve(int ql,int qr)
{
	if(ql==qr) return;
	int mid=(ql+qr)>>1;
	solve(ql,mid),solve(mid+1,qr);
	int nl=ql;
	for(int nr=mid+1;nr<=qr;nr++) if(op[nr].opt)
	{
		while(nl<=mid && op[nl].l<=op[nr].l)
			{ if(op[nl].opt==0) T.add(op[nl].r,op[nl].val); nl++; }
		ans[op[nr].num]+=T.query(op[nr].r);
	}
	for(int i=ql;i<nl;i++) if(op[i].opt==0) T.add(op[i].r,-op[i].val);
	inplace_merge(op.begin()+ql,op.begin()+mid+1,op.begin()+qr+1);
}
int main()
{
	n=rd(),m=rd();
	for(int i=1,opt,l,r,x;i<=m;i++)
	{
		opt=rd(),l=rd(),r=rd();
		if(opt==1) x=rd(),assign(x,l,r),ans[i]=-1;
		else op.emplace_back(1,l,r,0,i),ans[i]=0;
	}
	solve(0,op.size()-1);
	for(int i=1;i<=m;i++) if(ans[i]!=-1) printf("%lld\n",ans[i]);
	return 0;
}

于是,你已经对二维数点的基本原理有了一定的了解,就让我们来看一看下面这个简单的例子,来吧我们刚刚学到的知识运用到时间中吧。

试试看!(bushi

CF848C Goodbye Souvenir

修改一个元素其实就是删除一个元素后增加一个元素,分别考虑他们的贡献是多好:

设当前下标为 \(x\),上一个这种元素为 \(p\),下一个元素为 \(s\),则:

  • 加入一个元素:
    • \(l\in[1,p],r\in[x,s)\) 区间加上 \(x-p\)。
    • \(l\in(p,x],r\in[s,n]\) 区间加上 \(s-x\)。
  • 删除一个元素:将加法的权值倒过来即可。

我们需要查询的是区间和,即变为了一个矩形加、单点查询问题,离线解决即可。

P4690 [Ynoi2016] 镜中的昆虫

维护一个长为 \(n\) 的序列 \(a_i\),有 \(m\) 次操作。

  • 1 l r x 将区间 \([l,r]\) 的值修改为 \(x\)。
  • 2 l r 询问区间 \([l,r]\) 出现了多少种不同的数,也就是说同一个数出现多次只算一个。

\(1\le n,m\le 10^5,1\le a_i\le 10^9\)。

和上一题不能说差不多,只能说十分相似(bushi

用一个 ODT 维护全局颜色端,对每个颜色再分别维护颜色端。

用矩形加、单点查的套路做,分治解决即可。

标签:opt,le,P8441,val,int,数点,旭日东升,区间,emplace
来源: https://www.cnblogs.com/EricQian/p/16585834.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有