分块总结:时髦之裤
说白了就是南外分块题做的差不多了,来写一篇总结。简要题意: 给一序列 a,初始时 a i = i a_i=i ai=i,有如下两个操作:
1.将每个数改为x,该点增加贡献 ∣ a i − x ∣ |a_i-x| ∣ai−x∣.
2.扣问贡献和
样例:
10 4
1 2 7 9
2 1 10
1 3 8 12
2 1 10
27
46
范围1e5.
是的,假如不是刻意往分块上想,估计我怎么也想不到是分块。
留意到 ∣ a i − x ∣ |a_i-x| ∣ai−x∣,线段树不好做,带修莫队不太会,发现数据范围1e5, n n n\sqrt n nn 可以过,大胆思量分块,假设初始序列 a i = 0 a_i=0 ai=0,发现每次修改只会破坏两个块于是我们可以将颜色雷同的块与颜色不同的块分开修改,复杂度不超过 4 n 4\sqrt n 4n 。
是的,这就是正解。正确性显然,思量复杂度证明。
每个修改只会破坏两个块,故q次扣问只会破坏2q个块,加上由题意原来就破碎的 n \sqrt n n 个块,均摊复杂度不超过 5 n 5\sqrt n 5n ,常数够小,稳过。
从代码解释可以看出我有多急(
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+5;
int n,m,B;
LL bz,a,res,totres,sum,L,R,ta;//sum:one block,whole res:one block,broken.
void Turn(int l,int r,int x){
int ql=l/B;
if(bz) for(int i=l;i<=r;i++) res+=abs(a-x),totres+=abs(a-x),a=x;
else{
bz=1;
for(int i=L;i<=R;i++) a=ta; //ql->i once again!!
for(int i=l;i<=r;i++) res+=abs(ta-x),totres+=abs(ta-x),a=x;//ql->i debug 2h!!!
}
}
void update(int l,int r,int x){
int ql=l/B,qr=r/B;
if(ql==qr){
Turn(l,r,x);
return ;
}
Turn(l,(ql+1)*B-1,x);
Turn(qr*B,r,x);
for(int i=ql+1;i<qr;i++){
if(!bz){
sum+=abs(ta-x);
ta=x;
}else{
for(int j=i*B;j<(i+1)*B;j++) res+=abs(a-x),totres+=abs(a-x);
ta=x;
bz=0;
}
}
}
LL query(int l,int r){
int ql=l/B,qr=r/B;
LL ans=0;
if(ql==qr){
for(int i=l;i<=r;i++) ans+=res+sum;
return ans;
}
for(int i=l;i<(ql+1)*B;i++) ans+=res+sum;
for(int i=qr*B;i<=r;i++) ans+=res+sum;
for(int i=ql+1;i<qr;i++) ans+=sum*(R-L+1)+totres;
return ans;
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);B=sqrt(n);
for(int i=0;i<=n/B;i++) L=max(1,i*B),R=min(n,(i+1)*B-1);
// for(int i=1;i<=n;i++) printf("%d %d\n",L,R);
for(int i=1;i<=n;i++) a=i,bz=1;
while (m--){
int op,l,r;scanf("%d",&op);
if(op^2){
int x;scanf("%d%d%d",&l,&r,&x);
update(l,r,x);
}else{
scanf("%d%d",&l,&r);
// for(int i=l;i<=r;i++) printf("a:%d ta:%d res:%lld sum:%lld\n",a,ta,res,sum);
printf("%lld\n",query(l,r));
}
}
return 0;
}
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]