线段树之 HDU4578Transformation
2015-08-10 14:57
288 查看
这题,整整写了一天,公式不难推,但是加乘的先后顺序影响,整整debug了一天,而且是和ac了这题的队友一起找的。。
虽说这算基础题,但写起来确实很吃力,看来水平还不够,下面的解释也写得很含糊,有时候就有种只可意会不可言传的感觉。
虽说这算基础题,但写起来确实很吃力,看来水平还不够,下面的解释也写得很含糊,有时候就有种只可意会不可言传的感觉。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <stack> #include <queue> #include <vector> #include <map> #include <string> #include <sstream> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long LL; const double pi=4.0*atan(1.0); const int MAXN=100005; const int M=10007; // 代表一次方 平方 立方 LL sumx1[MAXN<<2],sumx2[MAXN<<2],sumx3[MAXN<<2]; // 加 乘 替换 lazy标记 LL add[MAXN<<2],mul[MAXN<<2],tihuan[MAXN<<2]; void PushUp(int rt) { sumx1[rt]=(sumx1[rt<<1]%M+sumx1[rt<<1|1]%M)%M; sumx2[rt]=(sumx2[rt<<1]%M+sumx2[rt<<1|1]%M)%M; sumx3[rt]=(sumx3[rt<<1]%M+sumx3[rt<<1|1]%M)%M; } void PushDown(int rt,int len) { //必须先替换,后乘法,再加法 if(tihuan[rt]!=-1) { tihuan[rt<<1]=tihuan[rt]%M; tihuan[rt<<1|1]=tihuan[rt]%M; sumx1[rt<<1]=tihuan[rt]%M*(len-(len>>1))%M; sumx1[rt<<1|1]=tihuan[rt]%M*(len>>1)%M; sumx2[rt<<1]=tihuan[rt]%M*tihuan[rt]%M*(len-(len>>1))%M; sumx2[rt<<1|1]=tihuan[rt]%M*tihuan[rt]%M*(len>>1)%M; sumx3[rt<<1]=tihuan[rt]%M*tihuan[rt]%M*tihuan[rt]%M*(len-(len>>1))%M; sumx3[rt<<1|1]=tihuan[rt]%M*tihuan[rt]%M*tihuan[rt]%M*(len>>1)%M; tihuan[rt]=-1; //这里坑了一天,首先如果替换了,那么当前的add[rt],mul[rt]里面的值不能修改! //可能会有疑惑,不是说替换就可以去掉加法和乘法的影响吗,确实是,但是不是在这里,此时如果add[rt],mul[rt]有值就代表先发生替换,后发生加乘 //发生先发生加乘后发生替换的话,在update时候就已经将add[rt],mul[rt]清空 //这里发生替换,需要将孩子节点的add[rt],mul[rt]清空 add[rt<<1]=0; add[rt<<1|1]=0; mul[rt<<1]=1; mul[rt<<1|1]=1; } LL x=1; if(mul[rt]!=1) { LL c=mul[rt]; //这里修改add,原理同update函数里的解释 add[rt<<1]=add[rt<<1]*c%M; add[rt<<1|1]=add[rt<<1|1]*c%M; mul[rt<<1]=(mul[rt<<1]%M*mul[rt]%M)%M; mul[rt<<1|1]=(mul[rt<<1|1]%M*mul[rt]%M)%M; sumx3[rt<<1]=(sumx3[rt<<1]%M*c%M*c%M*c%M)%M; sumx2[rt<<1]=(sumx2[rt<<1]%M*c%M*c%M)%M; sumx1[rt<<1]=(sumx1[rt<<1]%M*c%M)%M; sumx3[rt<<1|1]=(sumx3[rt<<1|1]%M*c%M*c%M*c%M)%M; sumx2[rt<<1|1]=(sumx2[rt<<1|1]%M*c%M*c%M)%M; sumx1[rt<<1|1]=(sumx1[rt<<1|1]%M*c%M)%M; mul[rt]=1; } if(add[rt]) { LL c=add[rt]%M; add[rt<<1]=(add[rt<<1]+c)%M; add[rt<<1|1]=(add[rt<<1|1]+c)%M; sumx3[rt<<1]=(sumx3[rt<<1]+(3*c%M*c%M*sumx1[rt<<1]%M+3*c*sumx2[rt<<1]%M)%M+(c%M*c%M*c%M)%M*(len-(len>>1)))%M; sumx2[rt<<1]=(sumx2[rt<<1]+2*c%M*sumx1[rt<<1]%M+(c%M*c%M)*(len-(len>>1))%M)%M; sumx1[rt<<1]=(sumx1[rt<<1]+c*(len-(len>>1))%M)%M; sumx3[rt<<1|1]=(sumx3[rt<<1|1]+(3*c%M*c%M*sumx1[rt<<1|1]%M+3*c*sumx2[rt<<1|1]%M)+(c*c*c)*(len>>1))%M; sumx2[rt<<1|1]=(sumx2[rt<<1|1]+(2*c%M*sumx1[rt<<1|1])+(c*c)*(len>>1)%M)%M; sumx1[rt<<1|1]=(sumx1[rt<<1|1]+c*(len>>1)%M)%M; add[rt]=0; } } void build(int l,int r,int rt) { add[rt]=0; mul[rt]=1; tihuan[rt]=-1; if(l==r) { sumx1[rt]=0; sumx2[rt]=0; sumx3[rt]=0; return ; } int m=(l+r)>>1; build(lson); build(rson); PushUp(rt); } void update(int kind,int L,int R,LL c,int l,int r,int rt) { if(L<=l &&r<=R) { if(kind==1)//加法 { //跟平时遇到的累加一样 add[rt]=(add[rt]+c)%M;//lazy标记add累加 sumx3[rt]=(sumx3[rt]+(3*c%M*c%M*sumx1[rt]%M+3*c*sumx2[rt]%M)+c*c%M*c%M*(r-l+1))%M; sumx2[rt]=(sumx2[rt]+(2*c%M*sumx1[rt])+(c*c%M)*(r-l+1)%M)%M; sumx1[rt]=(sumx1[rt]+c*(r-l+1)%M)%M; return ; } if(kind==2)//乘法 { //这里是重点,首先因为加法和乘法同时存在,所以两者之间的顺序关系会影响到结果 //当前是乘法,那么add标记里的值也要乘上该数字,否则我们必须要考虑加法和乘法的顺序,这样会相当复杂 //比如(x+3)*4 //先前add[rt]=3,此时我们进行*4,如果单单mul[rt]*=4,那么更新孩子节点时,就会发现不对了,更新成了x*4+3 //实际上(x+3)*4=4*x+12 //好吧,我也讲不清楚了 add[rt]=add[rt]*c%M; mul[rt]=(mul[rt]*c)%M; sumx3[rt]=(sumx3[rt]*c%M*c%M*c%M)%M; sumx2[rt]=(sumx2[rt]*c%M*c%M)%M; sumx1[rt]=(sumx1[rt]*c%M)%M; return ; } if(kind==3)//替换 { //替换的话,加法和乘法的标记都可以清空了 tihuan[rt]=c%M; sumx1[rt]=c%M*(r-l+1)%M; sumx2[rt]=c%M*c%M*(r-l+1)%M; sumx3[rt]=c%M*c%M*c%M*(r-l+1)%M; add[rt]=0; mul[rt]=1; return ; } } PushDown(rt,r-l+1); int m=(l+r)>>1; if(L<=m) update(kind,L,R,c,lson); if(m<R) update(kind,L,R,c,rson); PushUp(rt); } LL ans; void query(int L, int R,int kind,int l,int r,int rt) { if(L<=l &&r<=R) { if(kind==1) ans=(ans+sumx1[rt]%M)%M; else if(kind==2) ans=(ans+sumx2[rt]%M)%M; else if(kind==3) ans=(ans+sumx3[rt]%M)%M; return; } PushDown(rt,r-l+1); int m=(l+r)>>1; if(L<=m) query(L,R,kind,lson); if(m<R) query(L,R,kind,rson); } int main() { int n,m; int i,j,k; int x,y,z; int t; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0) return 0; build(1,n,1); int cnt=0; while(m--) { scanf("%d%d%d%d",&t,&x,&y,&z); if(t!=4) update(t,x,y,(LL)z,1,n,1); else { ans=0; query(x,y,z,1,n,1); ans%=M; printf("%I64d\n",ans); } } } return 0; }
相关文章推荐
- Android onTouch事件与手势操作
- hdu 1558
- jQuery 判断所有图片加载完成
- Codevs2370 小机房的树
- UIPasteboard Example – Read, Write and Share data between apps
- JS中Cookie详解及示例展示
- 内存分配方式
- 一个服务接口的多个实现的选择
- LCS 最大子段和,最大子段和在原数组的首末地址
- 【ecos】service
- hdu 3338 网络流填数字
- Java双缓冲技术
- apache tomcat集群
- 排列、组合相关知识
- 线段树(二)区间更新
- 静态代码块,构造代码块和构造函数的区别
- 中文分词与停用词的作用
- Java开发web的几种开发模式
- Retrofit 介绍
- apache tomcat集群