P8512 [Ynoi Easy Round 2021] TEST_152
题意
给定一个长度为 \(m\) 的序列,初始全为 \(0\)。再给 \(n\) 个区间赋值操作。
回答 \(q\) 次询问,每次询问给定 \(L,R\),表示从 \(L\) 到 \(R\) 执行完这 \(R-L+1\) 个操作,求全局和。
询问之间相互独立。
区间推平!直接 I Love Chtholly Tree!
然后考虑如何处理询问。询问是区间的形式,并且询问之间相互独立,所以考虑离线处理类似前缀的东西,或者按端点排序然后扫过去。
考虑 \(ODT\) 维护每次操作对全局和造成的影响,那么现在唯一剩下的问题就是如何处理时间戳。
结合上前面说的处理类似前缀的东西,考虑树状数组维护时间戳。相当于每次操作就是在对应的时间上单点修改记录全局和的变化,查询就是区间查询。
但是这样做的话不同时间的操作可能会互相影响。具体地,比如样例 \(1\) 的前两个操作,第一个是从 \(1\) 到 \(4\) 赋值为 \(3\),第二个是从 \(2\) 到 \(3\) 赋值为 \(1\)。这样的话,第二个操作的区间因为被包含在了第一个里面,所以它在原来基础上对全局和的影响其实并不是它本身带来的贡献,这个时候单独查询操作 \(2\) 就会出错。
所以考虑直接在 \(ODT\) 里面加上时间 \(t\) 一值,因为我们存下的每个颜色段一定对应唯一的一个时间,(即使有两个颜色相同但时间不同的连续段挨在一起,因为不是同一次操作产生的,所以我们也不会把它们合并在一起)那么每次 Assign
的时候就为每个颜色段对应的时间分别在树状数组上做修改即可。
最后一点就是把询问挂在右端点,离线扫过去用树状数组查询即可。
\(\text{Code}:\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define il inline
#define re register
const int N=500500;
int n,m,q,c[N],ans[N],l[N],r[N],x[N];
il int read(){
re int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
il int lowbit(int x){
return x&(-x);
}
il void Add(int x,int y){
if(x<=0)return;
while(x<=m){
c[x]+=y;
x+=lowbit(x);
}
}
il int Ask(int x){
int res=0;
while(x){
res+=c[x];
x-=lowbit(x);
}
return res;
}
struct Chtholly{
int l,r,t;
mutable int val;
Chtholly(int l,int r=0,int t=0,int val=0):l(l),r(r),t(t),val(val){}
bool operator<(Chtholly const &a)const{
return l<a.l;
}
};
set<Chtholly>s;
#define It set<Chtholly>::iterator
il It Split(int pos){
It fnd=s.lower_bound(Chtholly(pos));
if(fnd!=s.end()&&fnd->l==pos)return fnd;
fnd--;
if(fnd->r<pos)return s.end();
int l=fnd->l,r=fnd->r,t=fnd->t,val=fnd->val;
s.erase(fnd);
s.insert(Chtholly(l,pos-1,t,val));
return s.insert(Chtholly(pos,r,t,val)).first;
}//ODT 基本操作
il void Assign(int l,int r,int t,int x){
It R=Split(r+1),L=Split(l);
for(re It i=L;i!=R;i++)
Add(i->t,-(i->r-i->l+1)*i->val);//注意这里是减而不是加
s.erase(L,R);
s.insert(Chtholly(l,r,t,x));
Add(t,(r-l+1)*x);
}
struct query{
int l,id;
};
vector<query>v[N];
signed main(){
m=read(),n=read(),q=read();
s.insert(Chtholly(1,n,0,0));
for(re int i=1;i<=m;i++)
l[i]=read(),r[i]=read(),x[i]=read();
for(re int i=1;i<=q;i++){
int l=read(),r=read(),id=i;
v[r].push_back({l,id});//将询问挂在右端点
}
for(re int i=1;i<=m;i++){
Assign(l[i],r[i],i,x[i]);//离线扫描,查询答案。
for(re auto j:v[i])
ans[j.id]=Ask(i)-Ask(j.l-1);
}
for(re int i=1;i<=q;i++)
printf("%lld\n",ans[i]);
return 0;
}
题外话
-
纪念一下第一道 Ynoi Easy Round.(上次那个是 Ynoi 模拟赛,什么时候才能做正统 Ynoi 啊/kel
-
在图老师的逼迫下换了洛谷博客的主题和背景,还挺好看的感觉。