您的位置:首页 > 其它

soj 4018upit解题报告 splay经典应用

2012-08-25 01:04 302 查看
时隔一年,终于解决了upit。。。

先给个题目连接:soj:upit

题目大意:

维护一段序列,序列有4种操作:

1.将[A,B]的值统一赋值为X

2.将[A,B]的值每一个都添加X的若干倍,方法为第一个+X,之后的+2X,3X,.....,kX,如此递推

3.在第C个数之前插入数值X

4.求区间[A,B]的和。

具体参考如下:



序列初始长度为N,开始给出这N个数,之后又M个操作,其中N,M<=100,000 ,A,B均合法,X<=100(其实在64位范围就行了),结果在64位整型内。

分析:

这道题是个典型的序列维护的问题,且维护为区间块,有插入操作,需要惰性维护。

splay和块状链表都可以完成这种维护操作。

以下splay解:

①:插入,求和自不必说。。

②:将[A,B]整体赋值的操作,显然在维护的节点上加入两个域即可满足要求。这里记为clear(int),clFlag(bool)标记该子树是否被赋值,如果被赋值则为true

将clear赋值为-INF的时候表示未赋值可以省去变量clFlag

③:整体加法操作:

考虑之前有[A,B]+X,其子树记为R

后来因为R的旋转等因素将[A,B]分割成[A,C] + [C+1,B],这种时候A到C还是每个元素从+X,+2X,...,+kX,而C+1到B的部分则变成了+(k+1)X,....,+(k+m)X了,

也就是+kX+X,....,+kX+mX

其中k为子树[A,C]的节点数,m为[C+1,B]的节点数。

故每个区间由2个域构成,一个是基础的X,一个是附加的kx,分别记为addition和st

④:操作的组合

考虑对区间值进行操作的赋值和整体加法共2种

其组合无非是4种:赋值&赋值,赋值&加法,加法&加法,加法&赋值。

在区间维护的时候(lazy操作),第二次赋值显然要覆盖第一次赋值,先加法后赋值之前的加法也被覆盖

第二次加法累加到第一次加法上即可,还有就是先赋值后加法。

故在维护的时候有四种情况:

1.该节点仅有赋值,则赋值操作。

2.该节点仅有加法,则加法操作。

3.该节点均有,则必然是先赋值后加法,如果是先加法后赋值,则在上次维护的时候加法被赋值覆盖掉,仅剩赋值操作了。

4.均无。

ps:注意lazy操作,update更新sum,size等域的时候,前提是子树的数据sum和size已经是正确的。

在维护函数help的时候,计算保证该节点的sum和size是正确的,由下往上update。

故凡是在向下搜索节点的时候都要help,在伸展操作的时候要update。

注意update的前提,旋转后要先维护在update。

附个代码:

#include <stdio.h>
#include <ctype.h>

const int maxn = 200010;
const int INF = 0x7fffffff;
int root , n , m , k ;
typedef long long ll;
int array[maxn] ;

void help(int);

struct node
{
int l,r,p;
int size,clear;
ll add,st,sum,key;
void init()
{
l = p = r = add = st = sum = key = 0 ;
clear = -INF ;
size = 1 ;
}
} tree[maxn];

inline void update(int x)
{
if(!x) return ;
tree[x].size = tree[tree[x].l].size+tree[tree[x].r].size+1;
tree[x].sum = tree[tree[x].l].sum + tree[tree[x].r].sum + tree[x].key ;
}

inline int get()
{
int t = 0 ;
bool flag = 0 ;
char c;
while(!isdigit(c = getchar())&&c!='-');
if(c=='-') flag = 1;
else t = c - 48;
while(isdigit(c = getchar()))
t = (t << 1) + (t << 3) + c - 48;
return flag?-t:t;
}

inline void rotate(int x,int type)
{
int y = tree[x].p;
if(type)
{
tree[y].l = tree[x].r ;
tree[x].r = y ;
tree[tree[y].l].p = y ;
}
else
{
tree[y].r = tree[x].l ;
tree[x].l = y ;
tree[tree[y].r].p = y ;

}
tree[x].p = tree[y].p ;
tree[y].p = x ;
if( tree[tree[x].p].l == y ) tree[tree[x].p].l = x ;
else tree[tree[x].p].r = x ;
/*上次就是这个,这次还是!!!*/
help(tree[y].l);
help(tree[y].r);
update(y);
update(x);
}

inline void splay(int x,int rr)    //将x旋转到rr的儿子的位置
{
if( x == rr )    return ;
if( rr == 0 ) root = x ;
while(tree[x].p!=rr)
{
int y = tree[x].p ;
int z = tree[y].p ;
if( z == rr )    rotate(x,tree[y].l==x);
else
{
if( tree[z].l == y )
{
if(tree[y].l==x)
{
rotate(y,1);
rotate(x,1);
}
else
{
rotate(x,0);
rotate(x,1);
}
}
else
{
if(tree[y].r==x)
{
rotate(y,0);
rotate(x,0);
}
else
{
rotate(x,1);
rotate(x,0);
}
}
}
}
}

inline void help(int x)
{
if(!x)    return ;
if ( tree[x].clear != -INF )
{
if( tree[x].add == 0 )
{
tree[x].key = tree[x].clear ;
tree[x].sum = (ll)tree[x].size * tree[x].clear ;
tree[tree[x].r].clear = tree[tree[x].l].clear = tree[x].clear;
tree[tree[x].r].add = tree[tree[x].l].add = 0 ;
tree[tree[x].r].st = tree[tree[x].l].st = 0 ;
}
else
{
int xl = tree[x].l ;
int xr = tree[x].r ;
tree[x].key = (ll)tree[x].clear + (ll)( tree[xl].size + 1 )*tree[x].add + tree[x].st ;
tree[x].sum = (ll)(tree[x].clear+tree[x].st)*tree[x].size+(ll)tree[x].size*(tree[x].size+1)/2*tree[x].add;
tree[xl].clear = tree[xr].clear = tree[x].clear;
tree[xl].add = tree[xr].add = tree[x].add ;
tree[xl].st = tree[x].st ;
tree[xr].st = tree[x].st + (ll)(tree[xl].size+1)*tree[xl].add;
}
}
else
{
if( tree[x].add != 0 )
{
int xl = tree[x].l ;
int xr = tree[x].r ;
tree[x].key += (ll)(tree[xl].size+1)*tree[x].add+tree[x].st ;
tree[x].sum += (ll)tree[x].size * tree[x].st + (ll)tree[x].size * (tree[x].size+1) / 2 * tree[x].add ;
tree[xl].add += tree[x].add ;
tree[xl].st += tree[x].st ;
tree[xr].add += tree[x].add ;
tree[xr].st += (ll)(tree[xl].size+1)*tree[x].add + tree[x].st ;
}
}
tree[x].st = tree[x].add = 0 ;
tree[x].clear = -INF ;
}

void insert(int value,int pos);

inline void build(int l,int r)
{
/*k = 3 ;
tree[1].init();
tree[2].init();
tree[1].r = 2 ;
tree[2].p = 1 ;
update(1);
root = 1 ;
int i ;
for( i = n ; i >= 1 ; i--) insert(array[i],1);*/
int temp = k ;
//tree[k].init();
if( l == r )
{
tree[k].sum = tree[k].key = array[l-1] ;
tree[k].l = tree[k].r = 0 ;
tree[k].clear = -INF;
tree[k].add = tree[k].st = 0 ;
tree[k].size = 1 ;
}
else
{
int mid = (l+r)>>1 , j = k ;
tree[k].key = array[mid-1] ;
if(mid==l)    tree[k].l = 0 ;
else
{
tree[j].l = ++k;
tree[k].p = j ;
build(l,mid-1);
}
tree[j].r = ++k;
tree[k].p = j ;
build(mid+1,r);
tree[j].clear = -INF;
tree[j].add = tree[j].st = 0 ;
update(j);
}
}

inline int Rank(int key)    //查找第k个元素
{
int cur = root , j = 0 ;
while(1)
{
help(cur);
if( 1+tree[tree[cur].l].size+j == key )    return cur ;
else if( tree[tree[cur].l].size+j+1 < key )
{
j += (tree[tree[cur].l].size+1) ;
cur = tree[cur].r ;
}
else cur = tree[cur].l ;
}
}

inline ll querySum(int l,int r)
{
int temp = Rank(l);
int rr = Rank(r+2);
splay(temp,0);
splay(rr,temp);
help(temp);
help(rr);
help(tree[rr].l);
help(tree[tree[rr].l].l);
help(tree[tree[rr].l].r);
update(tree[rr].l);

return tree[tree[rr].l].sum ;
}

//在pos前插入数据value
inline void insert(int value,int pos)
{
int posTh = Rank(pos);
splay(posTh,0);
int temp = tree[posTh].r ;
help(temp);
while( tree[temp].l != 0 ) {    temp = tree[temp].l ; help(temp);   }
tree[temp].l = ++k;
int newnode = k ;
tree[newnode].key = value ;
tree[newnode].size = 1 ;
tree[newnode].sum = value ;
tree[newnode].l = tree[newnode].r = 0 ;
tree[newnode].p = temp ;
tree[newnode].clear = -INF;
tree[newnode].add = tree[newnode].st = 0 ;
splay(newnode,0);
}

void clear(int l,int r,int x)
{
int temp = Rank(l);
int tmp = Rank(r+2);
splay(temp,0);
splay(tmp,temp);
tree[tree[tmp].l].clear = x ;
tree[tree[tmp].l].add = 0 ;
}

void addInterval(int l,int r,int x)
{
int temp = Rank(l);
int tmp = Rank(r+2);
splay(temp,0);
splay(tmp,temp);
int interval = tree[tmp].l ;
tree[interval].add += x ;
}

inline void work()
{
int l , r , x , y , op ;
while(m--)
{
op = get();
if( op == 1 )
{
l = get();
r = get();
x = get();
clear(l,r,x);
}
else if( op == 4 )
{
l = get();
r = get();
printf("%lld\n",querySum(l,r));
}
else if( op == 3 )
{
x = get();
y = get();
insert(y,x);
}
else
{
l = get();
r = get();
x = get();
addInterval(l,r,x);
}
}
}

inline void getArray(char str[],int value[],int st)    //将字符串str中的数字写到value中去,自st开始,返回最后的num,无前导零
{
int i , j , t , negative ;
for( i = 0 , j = st ; str[i] ; i++) if((str[i]>=48&&str[i]<=57)||str[i]=='-')
{
if(str[i]=='-') negative = -1 , i++;
else negative = 1 ;
t = 0 ;
while(str[i]>=48&&str[i]<=57) t = (t<<1)+(t<<3)+(str[i++]^48) ;
value[j++] = t*negative ;
}
}

char str[maxn*5] ;
int main()
{
int i , j ;
while( scanf("%d%d",&n,&m) == 2 )
{
tree[0].init();
tree[0].size = 0 ;
tree[1].p = 0 ;
root = k = 1 ;
getchar();
gets(str);
getArray(str,array,1);

build(1,n+2);
work();
}
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: