题目链接:http://poj.org/problem?id=3468
以前用线段树做过,现在用Splay Tree A了,向HH、kuangbin、cxlove大牛学习了各种Splay各种操作,,,Orz。。
Splay Tree的区间操作和线段树的操作差不多,也是保存子树的值,然后懒惰操作,在Rotate()最后维护节点信息的时候,只要Push_Up(y)的,因为x还需要网上旋转到根节点,最后更新下就可以了,并且在下一次Rotate()的时候,还会Push_Down(x)的信息,因此不能Push_Up(x)。
1 //STATUS:C++_AC_3407MS_3696KB 2 #include <functional> 3 #include <algorithm> 4 #include <iostream> 5 //#include <ext/rope> 6 #include <fstream> 7 #include <sstream> 8 #include <iomanip> 9 #include <numeric> 10 #include <cstring> 11 #include <cassert> 12 #include <cstdio> 13 #include <string> 14 #include <vector> 15 #include <bitset> 16 #include <queue> 17 #include <stack> 18 #include <cmath> 19 #include <ctime> 20 #include <list> 21 #include <set> 22 #include <map> 23 using namespace std; 24 //using namespace __gnu_cxx; 25 //define 26 #define pii pair<int,int> 27 #define mem(a,b) memset(a,b,sizeof(a)) 28 #define lson l,mid,rt<<1 29 #define rson mid+1,r,rt<<1|1 30 #define PI acos(-1.0) 31 //typedef 32 typedef __int64 LL; 33 typedef unsigned __int64 ULL; 34 //const 35 const int N=100010; 36 const int INF=0x3f3f3f3f; 37 const int MOD=100000,STA=8000010; 38 const LL LNF=1LL<<60; 39 const double EPS=1e-8; 40 const double OO=1e15; 41 const int dx[4]={-1,0,1,0}; 42 const int dy[4]={0,1,0,-1}; 43 const int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; 44 //Daily Use ... 45 inline int sign(double x){return (x>EPS)-(x<-EPS);} 46 template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;} 47 template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;} 48 template<class T> inline T lcm(T a,T b,T d){return a/d*b;} 49 template<class T> inline T Min(T a,T b){return a<b?a:b;} 50 template<class T> inline T Max(T a,T b){return a>b?a:b;} 51 template<class T> inline T Min(T a,T b,T c){return min(min(a, b),c);} 52 template<class T> inline T Max(T a,T b,T c){return max(max(a, b),c);} 53 template<class T> inline T Min(T a,T b,T c,T d){return min(min(a, b),min(c,d));} 54 template<class T> inline T Max(T a,T b,T c,T d){return max(max(a, b),max(c,d));} 55 //End 56 57 #define Key_value ch[ch[root][1]][0] 58 int pre[N],key[N],ch[N][2]; //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量 59 int sz[N],st[N]; //子树规模,内存池 60 int root,tot,top; //根节点,根节点数量,内存池容量 61 //题目特定数据 62 int num[N]; 63 int val[N]; 64 int add[N]; 65 LL sum[N]; 66 int n,m; 67 //debug部分copy from hh 68 void Treaval(int x) { 69 if(x) { 70 Treaval(ch[x][0]); 71 printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2d \n",x,ch[x][0],ch[x][1],pre[x],sz[x],val[x],sum[x]); 72 Treaval(ch[x][1]); 73 } 74 } 75 void debug() {printf("%d\n",root);Treaval(root);} 76 //以上Debug 77 //新建一个结点 78 void NewNode(int &x,int fa,int k) 79 { 80 if(top)x=st[--top]; 81 else x=++tot; 82 pre[x]=fa; 83 sz[x]=1; 84 val[x]=k; 85 add[x]=0; 86 sum[x]=k; 87 ch[x][0]=ch[x][1]=0; //左右孩子为空 88 } 89 90 void Push_Up(int x) 91 { 92 sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; 93 sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x]; 94 } 95 96 void Push_Down(int x) 97 { 98 if(add[x]){ 99 val[x]+=add[x]; 100 add[ch[x][0]]+=add[x]; 101 add[ch[x][1]]+=add[x]; 102 sum[ch[x][0]]+=(LL)add[x]*sz[ch[x][0]]; 103 sum[ch[x][1]]+=(LL)add[x]*sz[ch[x][1]]; 104 add[x]=0; 105 } 106 } 107 108 //旋转,kind为1为右旋,kind为0为左旋 109 void Rotate(int x,int kind) 110 { 111 int y=pre[x],z=pre[y]; 112 Push_Down(y); 113 Push_Down(x); //先把y的标记向下传递,再把x的标记往下传递 114 //类似SBT,要把其中一个分支先给父节点 115 ch[y][!kind]=ch[x][kind]; 116 pre[ch[x][kind]]=y; 117 //如果父节点不是根结点,则要和父节点的父节点连接起来 118 if(z)ch[z][ch[z][1]==y]=x; 119 pre[x]=z; 120 ch[x][kind]=y; 121 pre[y]=x; 122 Push_Up(y); //维护y结点,不要维护x节点,x节点会再次Push_Down,最后维护一下x节点即可 123 } 124 //Splay调整,将根为r的子树调整为goal 125 void Splay(int x,int goal) 126 { 127 int y,kind; 128 while(pre[x]!=goal){ 129 //父节点即是目标位置,goal为0表示,父节点就是根结点 130 y=pre[x]; 131 if(pre[y]==goal){ 132 Rotate(x,ch[y][0]==x); 133 } 134 else { 135 kind=ch[pre[y]][0]==y; 136 //两个方向不同,则先左旋再右旋 137 if(ch[y][kind]==x){ 138 Rotate(x,!kind); 139 Rotate(x,kind); 140 } 141 //两个方向相同,相同方向连续两次 142 else { 143 Rotate(y,kind); 144 Rotate(x,kind); 145 } 146 } 147 } 148 //更新根结点 149 Push_Up(x); 150 if(goal==0)root=x; 151 } 152 153 void RotateTo(int k,int goal) 154 { 155 int x=root; 156 Push_Down(x); 157 while(sz[ch[x][0]]!=k){ 158 if(sz[ch[x][0]]>k) 159 x=ch[x][0]; 160 else { 161 k-=sz[ch[x][0]]+1; 162 x=ch[x][1]; 163 } 164 Push_Down(x); 165 } 166 Splay(x,goal); 167 } 168 169 int Insert(int k) 170 { 171 int x=root; 172 while(ch[x][k>key[x]]){ 173 //不重复插入 174 if(key[x]==k){ 175 Splay(x,0); 176 return 0; 177 } 178 x=ch[x][k>key[x]]; 179 } 180 NewNode(ch[x][k>key[x]],x,k); 181 //将新插入的结点更新至根结点 182 Splay(ch[x][k>key[x]],0); 183 return 1; 184 } 185 //找前驱,即左子树的最右结点 186 int Get_Pre(int x) 187 { 188 if(!ch[x][0])return -INF; 189 x=ch[x][0]; 190 while(ch[x][1])x=ch[x][1]; 191 return key[x]; 192 } 193 //找后继,即右子树的最左结点 194 int Get_Suf(int x) 195 { 196 if(!ch[x][1])return INF; 197 x=ch[x][1]; 198 while(ch[x][0])x=ch[x][0]; 199 return key[x]; 200 } 201 //建树,中间结点先建立,然后分别对区间两端在左右子树建立 202 void BuildTree(int &x,int l,int r,int fa) 203 { 204 if(l>r)return; 205 int mid=(l+r)>>1; 206 NewNode(x,fa,num[mid]); 207 BuildTree(ch[x][0],l,mid-1,x); 208 BuildTree(ch[x][1],mid+1,r,x); 209 Push_Up(x); 210 } 211 212 void Init() 213 { 214 ch[0][0]=ch[0][1]=pre[0]=sz[0]=0; 215 add[0]=sum[0]=0; 216 root=top=tot=0; 217 NewNode(root,0,-1); 218 NewNode(ch[root][1],root,-1); //头尾各加入一个空位 219 sz[root]=2; 220 221 for(int i=0;i<n;i++) 222 scanf("%d",&num[i]); 223 BuildTree(Key_value,0,n-1,ch[root][1]); //让所有数据夹在两个-1之间 224 Push_Up(ch[root][1]); 225 Push_Up(root); 226 } 227 228 void Update(int a,int b,int c) 229 { 230 RotateTo(a-1,0); 231 RotateTo(b+1,root); 232 add[Key_value]+=c; 233 sum[Key_value]+=sz[Key_value]*c; 234 } 235 236 LL Query(int a,int b) 237 { 238 RotateTo(a-1,0); 239 RotateTo(b+1,root); 240 return sum[Key_value]; 241 } 242 243 int main() 244 { 245 // freopen("in.txt","r",stdin); 246 int i,j,a,b,c; 247 char op[2]; 248 while(~scanf("%d%d",&n,&m)) 249 { 250 Init(); 251 while(m--){ 252 scanf("%s",op); 253 if(op[0]=='Q'){ 254 scanf("%d%d",&a,&b); 255 printf("%I64d\n",Query(a,b)); 256 } 257 else { 258 scanf("%d%d%d",&a,&b,&c); 259 Update(a,b,c); 260 } 261 } 262 } 263 return 0; 264 }