您的位置:首页 > 其它

Codeforces444C DZY Loves Colors

2016-06-27 20:40 239 查看
题目链接

题目大意

给定一个长度为n的序列,初始时ai=i,vali=0(1≤i≤n).有两种操作:

将区间[L,R]的值改为x,并且当一个数从y改成x时它的权值vali会增加|x−y|.

询问区间[L,R]的权值和.

n≤105,1≤x≤106.

题解

这种区间问题最初的想法肯定是用线段树.

但是这题的区间修改操作似乎不管维护什么信息都不好高效地进行维护.

这时发挥我们的聪明才智就可以想到一个巧妙的”剪枝”:

每个节点再维护一个same,表示这个区间的颜色均为same(或者设一个特殊值表示不全相同).

之所以这样,是因为考虑到把一个颜色相同的区间染成一个颜色是很容易的.

然后我们的修改操作就变成了这样:

一直走到颜色相同的区间才更新并回溯,否则一直递归更新.

跑一遍意识流就感觉这个复杂度均摊应该不高...

网上找不到什么证明,于是我简略说一下我的看法.

询问是一般的O(lgn),考虑更新的复杂度.

不妨设一开始所有颜色都相等,这个应该不会影响操作的均摊复杂度.

考虑更新时如果把一个节点对应的区间变成了颜色不全相同,那么这个节点的所有父亲(或者说长辈?)节点都会变成颜色不全相同.

那么下次更新时就必须沿着这些父亲节点走下来,而这个过程可以看做是这次更新的一次回溯,可以理解成把下次更新的复杂度均摊到这次操作,所以总的复杂度是O(mlgn)的.

在此感谢ShinFeb为我解惑.

当然这样解释似乎还是不太严谨,期待严谨的证明.

代码

#include<cstdio>
#include<algorithm>
#define lson (k<<1)
#define rson (k<<1|1)
using namespace std;
const int N=1e5+5;
typedef long long ll;
struct Segment_Tree{
struct Node{
int L,R,same;
ll tag,sum;
inline int len(){
return R-L+1;
}
}tree[N<<2];
void build(int L,int R,int k=1){
tree[k]=(Node){L,R,-1,0,0};
if(L==R){
tree[k].same=L;
return;
}
int mid=L+R>>1;
build(L,mid,lson);
build(mid+1,R,rson);
}
inline void push_up(int k){
tree[k].sum=tree[lson].sum+tree[rson].sum;
if(tree[lson].same!=tree[rson].same)tree[k].same=-1;
else tree[k].same=tree[lson].same;
}
void update(int k,ll val,int col){
tree[k].tag+=val;
tree[k].sum+=1ll*tree[k].len()*val;
tree[k].same=col;
}
void push_down(int k){
if(!tree[k].tag)return;
update(lson,tree[k].tag,tree[k].same);
update(rson,tree[k].tag,tree[k].same);
tree[k].tag=0;
}
void modify(int L,int R,int val,int k=1){
if(tree[k].L==L&&tree[k].R==R&&~tree[k].same){
update(k,abs(val-tree[k].same),val);
return;
}
int mid=tree[k].L+tree[k].R>>1;
push_down(k);
if(R<=mid)modify(L,R,val,lson);
else if(L>mid)modify(L,R,val,rson);
else{
modify(L,mid,val,lson);
modify(mid+1,R,val,rson);
}
push_up(k);
}
ll query(int L,int R,int k=1){
if(tree[k].L==L&&tree[k].R==R)
return tree[k].sum;
int mid=tree[k].L+tree[k].R>>1;
push_down(k);
if(R<=mid)return query(L,R,lson);
if(L>mid)return query(L,R,rson);
return query(L,mid,lson)+query(mid+1,R,rson);
}
}T;
void rd(int &res){
res=0;
char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>47);
}
int main(){
int n,m,ope,L,R,val;
rd(n);rd(m);
T.build(1,n);
while(m--){
rd(ope);rd(L);rd(R);
if(ope==1){
rd(val);
T.modify(L,R,val);
}
else printf("%I64d\n",T.query(L,R));
}
return 0;
}
/*

Jun.26.16

Tags:Segment Tree
Submissions:5

Time 187ms
Memory 12300KB

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  codeforces 线段树