【HNOI2016】序列(线段树)

题意扩展版:有两个长度为 n n n 的序列 a , b a,b a,b,你需要维护 m m m 次操作:

  • a a a 区间赋值。
  • 给定 l , r l,r l,r,对于所有 i ∈ [ l , r ] i\in[l,r] i[l,r],执行 b i ← b i + a i b_i\gets b_i+a_i bibi+ai
  • 询问区间 b b b 的和。

题解:

UPD:可以线段树上每个点维护矩阵 [ s u m a , s u m b , 1 ] [suma,sumb,1] [suma,sumb,1] 简单做到 O ( n log ⁡ n ) O(n\log n) O(nlogn)

感觉整篇题解都有点表达不清,但是我也不清楚应该怎样表达才好。

考虑使用线段树,发现操作一和操作二的懒标记是不好合并的,因为会出现两个操作二中间夹着一个操作一的情况。

但我们可以这样:进行操作一时,只有当前节点代表的区间中的所有 a i a_i ai 都相同时才更新并结束递归。

这时操作二的懒标记就可以变成区间加的标记:因为这个区间的 a i a_i ai 都相同,所以对这个区间进行 t t t 次操作二相当于对这个区间的每一个 b i b_i bi 都加上 a i t a_it ait

这时只需要维护这几个标记即可:区间加、 a a a 区间赋值标记、操作二次数标记。规定先执行 a a a 区间赋值标记再执行操作二次数标记,而且保证当 a a a 区间赋值标记存在时,当前区间原来的 a i a_i ai 都是相同的。

代数的理解是每个位置维护了一条直线 y = a i x + c y=a_ix+c y=aix+c,操作二相当于是区间 x x x 加一,而对于区间 a i a_i ai 赋值,若原本区间内所有的 a i a_i ai 都相等,你会发现这个区间内所有的直线的 c c c 的变化量都是相同的。这时三个标记对应着:区间直线截距( c c c)加、区间直线斜率( a i a_i ai)赋值、区间 x x x 加一。

考虑这么做的时间复杂度,瓶颈在于进行操作一时遍历线段树的时间。

S S S 表示 a a a 序列中的极长相等段个数,规定这里的 “段” 必须能被线段树上的某个节点所代表。初始时 S S S O ( n ) O(n) O(n) 级别。

考虑一次操作一的过程:它会在线段树上访问到 a a a 序列中连续的若干个相等段 p 1 , ⋯   , p k p_1,\cdots,p_k p1,,pk,时间复杂度为 O ( k log ⁡ n ) O(k\log n) O(klogn),但是访问完之后 p 1 , ⋯   , p k p_1,\cdots,p_k p1,,pk 将会合并为至多 O ( log ⁡ n ) O(\log n) O(logn) 个极长相等段, S S S 至少会减少 O ( k − log ⁡ n ) O(k-\log n) O(klogn)。但由于 p 1 p_1 p1 p k p_k pk 原来不一定是极长的相等段,访问到它们的过程中会把它们原本所属的极长相等段各拆成至多 O ( log ⁡ n ) O(\log n) O(logn) 个极长相等段,所以 S S S 又会增加至多 O ( 2 log ⁡ n ) O(2\log n) O(2logn)。所以总的来说, S S S 还是至少会减少 O ( k − log ⁡ n ) O(k-\log n) O(klogn)

由于 S S S 应该满足始终大于 0 0 0,所以 n − ∑ i = 1 m ( k − log ⁡ n ) > 0 n-\sum_{i=1}^m(k-\log n)>0 ni=1m(klogn)>0,于是 ∑ i = 1 m k < n + m log ⁡ n \sum_{i=1}^m k<n+m\log n i=1mk<n+mlogn,于是总时间复杂度为 O ( ( n + m log ⁡ n ) log ⁡ n ) = O ( n log ⁡ n + m log ⁡ 2 n ) O((n+m\log n)\log n)=O(n\log n+m\log ^2n) O((n+mlogn)logn)=O(nlogn+mlog2n)

#include<bits/stdc++.h>

#define ll long long
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
#define N 100010

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

ll ans[N];
int n,Q,a[N];
int top,sta[N];
ll sum[N<<2],suma[N<<2],add[N<<2];
int na[N<<2],tim[N<<2],reset[N<<2];
bool tag[N<<2];

vector<pii>q[N];

void up(int k)
{
	sum[k]=sum[k<<1]+sum[k<<1|1];
	suma[k]=suma[k<<1]+suma[k<<1|1];
	na[k]=na[k<<1];
}

void downa(int k,int nowa,int l,int r)
{
	add[k]+=1ll*tim[k]*na[k],tim[k]=0;
	tag[k]=1,na[k]=reset[k]=nowa,suma[k]=1ll*(r-l+1)*nowa;
}

void downt(int k,int t)
{
	sum[k]+=1ll*suma[k]*t;
	tim[k]+=t;
}

void downn(int k,ll v,int l,int r)
{
	sum[k]+=1ll*(r-l+1)*v;
	add[k]+=v;
}

void down(int k,int l,int r,int mid)
{
	if(tag[k])
	{
		downa(k<<1,reset[k],l,mid);
		downa(k<<1|1,reset[k],mid+1,r);
		tag[k]=0;
	}
	if(tim[k])
	{
		downt(k<<1,tim[k]);
		downt(k<<1|1,tim[k]);
		tim[k]=0;
	}
	if(add[k])
	{
		downn(k<<1,add[k],l,mid);
		downn(k<<1|1,add[k],mid+1,r);
		add[k]=0;
	}
}

void update(int k,int l,int r,int ql,int qr,int nowa)
{
	if(ql<=l&&r<=qr)
	{
		downa(k,nowa,l,r);
		return;
	}
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	if(ql<=mid) update(k<<1,l,mid,ql,qr,nowa);
	if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,nowa);
	up(k);
}

ll query(int k,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr) return sum[k];
	int mid=(l+r)>>1;
	down(k,l,r,mid);
	ll ans=0;
	if(ql<=mid) ans+=query(k<<1,l,mid,ql,qr);
	if(qr>mid) ans+=query(k<<1|1,mid+1,r,ql,qr);
	return ans;
}

int main()
{
	n=read(),Q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=Q;i++)
	{
		int l=read(),r=read();
		q[r].push_back(mk(l,i));
	}
	for(int i=1;i<=n;i++)
	{
		update(1,1,n,sta[top]+1,i,a[i]);
		while(top&&a[sta[top]]>a[i])
		{
			update(1,1,n,sta[top-1]+1,sta[top],a[i]);
			top--;
		}
		sta[++top]=i;
		downt(1,1);
		for(pii que:q[i])
			ans[que.se]=query(1,1,n,que.fi,i);
	}
	for(int i=1;i<=Q;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

另一个很像的例题:【XSY3813】漏网之鱼

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值