ICode9

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

CF997E Good Subsegments

2021-11-15 19:03:55  阅读:177  来源: 互联网

标签:Subsegments int CF997E 贡献 最小值 Good 端点 区间 t1


一、题目

点此看题

二、解法

这题真的比较清新而且有意思,你可以先做一下弱化版

这种区间套区间的问题猫树是通用解法,但是需要 \(O(n\log^2n)\) 而且口味很重,你可以把这道题的猫树解法魔改一下。

更好的做法还是移动右端点,维护每个左端点的历史答案,也就是当这个左端点被激活后每个右端点的贡献都要记录在这个点上,询问的时候把询问区间的所有左端点贡献求和就可以得到答案。本题我们可以考虑维护这个贡献和,对于一个新的右端点我们在根上打一个标记 \(tim\)

那么根的贡献和会增加全局最小值个数,然后我们考虑如何下传,如果当前节点和儿子的最小值一样,就把儿子的贡献增加它的最小值个数。现在只考虑这个统计贡献操作是没有问题的,但是如果和区间加法标记混用会不会出问题呢?你发现区间加法对统计贡献是不影响的,因为最小值的分布情况是不变的。

那么把询问离线下来做即可,时间复杂度 \(O(n\log n)\)

三、总结

这种区间套区间问题,历史思想很重要,我至今见过两种用法:

  • 如果记录历史操作和修改操作相互干扰,可以尝试使用矩阵乘法避免复杂讨论。
  • 如果两种操作之间互不干扰,并且需要先判定再记录贡献,可以直接下传标记。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 120005;
const int N = M*4;
const int inf = 0x3f3f3f3f;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,t1,t2,a[M],s1[M],s2[M];ll ans[M],sum[4*M];
int mi[4*M],num[4*M],fl[4*M],tim[4*M];
struct node
{
	int l,r,id;
	bool operator < (const node &b) const
	{
		return r<b.r;
	}
}q[M];
void contri(int i,int x)
{
	if(!x) return ;
	sum[i]+=1ll*x*num[i];tim[i]+=x;
}
void build(int i,int l,int r)
{
	mi[i]=inf;num[i]=r-l+1;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
}
void down(int i)
{
	if(fl[i])
	{
		mi[i<<1]+=fl[i];fl[i<<1]+=fl[i];
		mi[i<<1|1]+=fl[i];fl[i<<1|1]+=fl[i];
		fl[i]=0;
	}
	if(tim[i])
	{
		if(mi[i]==mi[i<<1]) contri(i<<1,tim[i]);
		if(mi[i]==mi[i<<1|1]) contri(i<<1|1,tim[i]);
		tim[i]=0;
	}
}
void add(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R)
	{
		fl[i]+=c;mi[i]+=c;
		return ;
	}
	int mid=(l+r)>>1;down(i);
	add(i<<1,l,mid,L,R,c);
	add(i<<1|1,mid+1,r,L,R,c);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);num[i]=0;
	if(mi[i]==mi[i<<1]) num[i]+=num[i<<1];
	if(mi[i]==mi[i<<1|1]) num[i]+=num[i<<1|1]; 
}
ll ask(int i,int l,int r,int L,int R)
{
	if(L>r || l>R) return 0;
	if(L<=l && r<=R) return sum[i];
	int mid=(l+r)>>1;down(i);
	return ask(i<<1,l,mid,L,R)
	+ask(i<<1|1,mid+1,r,L,R);
}
signed main()
{
	n=read();build(1,1,n);
	for(int i=1;i<=n;i++)
		a[i]=read();
	m=read();
	for(int i=1;i<=m;i++)
		q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+1+m);
	for(int i=1,j=1;i<=n;i++)
	{
		add(1,1,n,i,i,-inf+i);
		while(t1 && a[s1[t1]]>=a[i])
		{
			add(1,1,n,s1[t1-1]+1,s1[t1],a[s1[t1]]);
			t1--;
		}
		while(t2 && a[s2[t2]]<=a[i])
		{
			add(1,1,n,s2[t2-1]+1,s2[t2],-a[s2[t2]]);
			t2--;
		}
		add(1,1,n,s1[t1]+1,i,-a[i]);
		add(1,1,n,s2[t2]+1,i,a[i]);
		s1[++t1]=i;s2[++t2]=i;
		contri(1,1);
		while(j<=m && q[j].r==i)
		{
			ans[q[j].id]=ask(1,1,n,q[j].l,i);
			j++;
		}
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
}

标签:Subsegments,int,CF997E,贡献,最小值,Good,端点,区间,t1
来源: https://www.cnblogs.com/C202044zxy/p/15557725.html

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

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

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

ICode9版权所有