链接:https://ac.nowcoder.com/acm/contest/892/D
来源:牛客网
题目描述
给你一个长度为n的序列,初始为1,2,3...n,对其进行m次操作。
操作有两种:
1 l r 表示将区间[l,r]用 [1,2...r-l+1] 覆盖
2 l r 查询[l,r]的区间和
输入描述:
第一行包含2个数字,n,m(1 <= n,m <= 1e5)
接下来包含m行,格式如题面所示
输出描述:
对于每个操作2,输出一行一个整数表示答案
示例1
输入
10 5 2 1 10 1 3 6 2 1 10 1 1 10 2 1 10
输出
55 47 55
题意:很明显了,读题目就行。
数据范围:1e5的数据,1e5次操作,听别人说分块T了,那还是用线段树吧。
因为题目的两次操作不就是区间查找,区间更新吗。然而与以往的区间更新不同,这次区间更新要求的是将区间[l,r]更新为[1,2,3,..,r-l+1]
那么我们可以把懒人标记记录的值设定为当前更新区间的左端点和右端点更新后的值,然后再次更新或查找时pushdown。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5; ll tree[maxn*4]; ll lazy[maxn*4][2]; void pushdown(int root,int l,int r) { if(lazy[root][0]!=0) { int mid=lazy[root][0]+lazy[root][1]>>1; tree[root<<1]=(lazy[root][0]+mid)*(mid-lazy[root][0]+1)/2; tree[root<<1|1]=(lazy[root][1]+mid+1)*(lazy[root][1]-mid)/2; lazy[root<<1][0]=lazy[root][0]; lazy[root<<1][1]=mid; lazy[root<<1|1][1]=lazy[root][1]; lazy[root<<1|1][0]=mid+1; lazy[root][0]=0; lazy[root][1]=0; } } void update(int root,int l,int r,int ql,int qr) { if(ql<=l&&qr>=r) { tree[root]=1ll*(l-ql+1+l-ql+1+r-l)*(r-l+1)/2; lazy[root][0]=l-ql+1; lazy[root][1]=l-ql+1+r-l; return; } pushdown(root,l,r); int mid=l+r>>1; if(ql<=mid) update(root<<1,l,mid,ql,qr); if(qr>mid) update(root<<1|1,mid+1,r,ql,qr); tree[root]=tree[root<<1|1]+tree[root<<1]; } ll querry(int root,int l,int r,int ql,int qr) { if(l>=ql&&r<=qr) { return tree[root]; } int mid=l+r>>1; pushdown(root,l,r); ll ans=0; if(ql<=mid) ans+=querry(root<<1,l,mid,ql,qr); if(qr>mid) ans+=querry(root<<1|1,mid+1,r,ql,qr); return ans; } void build(int root,int l,int r) { if(l==r) { tree[root]=l; lazy[root][0]=0; return; } int mid=l+r>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); tree[root]=tree[root<<1]+tree[root<<1|1]; } int main() { int n,m; scanf("%d%d",&n,&m); build(1,1,n); while(m--) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(a==2) { ll ans=querry(1,1,n,b,c); printf("%lld\n",ans); } else { update(1,1,n,b,c); } } return 0; }