HDU 4578 Transformation(线段树+做了4天的神题+详解)
来源:互联网 发布:微博域名修改 编辑:程序博客网 时间:2024/05/24 06:03
There are n integers, a 1, a 2, …, a n. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between a x and a y inclusive. In other words, do transformation a k<---a k+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between a x and a y inclusive. In other words, do transformation a k<---a k×c, k = x,x+1,…,y.
Operation 3: Change the numbers between a x and a y to c, inclusive. In other words, do transformation a k<---c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between a x and a y inclusive. In other words, get the result of a x p+a x+1 p+…+a y p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him.
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
Each the following m lines contains an operation. Operation 1 to 3 is in this format: "1 x y c" or "2 x y c" or "3 x y c". Operation 4 is in this format: "4 x y p". (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
The input ends with 0 0.
5 53 3 5 71 2 4 44 1 5 22 2 5 84 3 5 30 0
3077489
题解:
我只能说这题很迷,迷之WA,这题我陆陆续续做了4天才AC,其中WA了3页,共计40多次吧,重写代码3次,用各种不同方法写。。最后无奈看着别人的博客照着一个大佬的写就AC了。。吐血,一开始我打算用3个v,3个tag来写,3个v表示区间的一二三次方值,tag分别表示加,乘等于的标记,先处理等,如果有等号标记,区间更新,清除加号和乘号标记,在处理乘和加之前看子区间有没有等号标记,有就先处理子区间的等号标记,然后处理乘,如果同时有加号标记把加的值乘上一个要乘的数,最后处理乘号标记,至于一次方二次方三次方公式是搞数学的老刘推的。。结果就是我样例过了,自己测的几组也过了还是WA,各种迷,怎么修改都是WA,后来看到一个大佬的没有推公式也没有储存三次方。。方法很神奇,而且速度比一般用储存3个值的那种快得多。。
说一下思路:
同样tag1表示加号标记,tag2表示乘,tag3表示等,无疑在向下更新的时候等号的优先级应该最高,先处理等号标记,并把加号和乘号标记置为0和1(初始值),然后重点来了,在处理乘号标记的时候,如果发现子区间有等号标记,直接修改等号标记,否则先将子区间pushdwon一下清空标记,再进行该子区间标记,加号同理,如果子区间有等号标记,就修改等号标记,否则将子区间pushdown清空标记,再将该子区间标记,所有操作完都要清空当前区间标记,然后修改的时候也差不多,先检查该区间有没有等号标记,有的话直接修改等号标记,否则pushdown清空该区间标记,再进行各种标记,查询的时候是整个算法的精髓,他是查询到要查询区间的子区间的等号标记不为初始标记,即为一段相同的数字的时候停止,然后返回该区间的值,进行各个子区间累加,得出答案。。。。一开始我还以为会超时,结果居然比一般的算法快得多,膜拜大佬
大佬博客:http://blog.csdn.net/cq_pf/article/details/47054667
代码:
#include<algorithm>#include<iostream>#include<cstring>#include<stdio.h>#include<math.h>#include<string>#include<stdio.h>#include<queue>#include<stack>#include<map>#include<deque>using namespace std;#define left k*2#define right k*2+1const int N=10007;struct node{ int l,r; int tag1,tag2,tag3;//分别表示加号,乘号,等号标记}t[100005*4];int n;void Build(int l,int r,int k){ t[k].l=l; t[k].r=r; t[k].tag1=0; t[k].tag2=1; t[k].tag3=-1; if(l==r) { t[k].tag3=0;//最底层要赋值为0 return; }; int mid=(l+r)/2; Build(l,mid,left); Build(mid+1,r,right);}void pushdown(int k){ if(t[k].l==t[k].r)//没有子区间了不用退了 return; if(t[k].tag3!=-1)//处理等号 { t[left].tag3=t[right].tag3=t[k].tag3;//更新子区间等号标记 t[left].tag2=t[right].tag2=1; t[left].tag1=t[right].tag1=0;//清空子区间加乘标记 t[k].tag3=-1; return; } if(t[k].tag2!=1)//处理乘号 { if(t[left].tag3!=-1)//如果子区间有等号标记,直接修改等号标记 t[left].tag3=(t[left].tag3*t[k].tag2)%N; else//否则清空该子区间标记,进行子区间标记 { pushdown(left); t[left].tag2=(t[left].tag2*t[k].tag2)%N; } if(t[right].tag3!=-1)//同理处理右区间 t[right].tag3=(t[right].tag3*t[k].tag2)%N; else { pushdown(right); t[right].tag2=(t[right].tag2*t[k].tag2)%N; } t[k].tag2=1; } if(t[k].tag1!=0)//处理加号标记,和上面同理处理 { if(t[left].tag3!=-1) t[left].tag3=(t[left].tag3+t[k].tag1)%N; else { pushdown(left); t[left].tag1=(t[left].tag1+t[k].tag1)%N; } if(t[right].tag3!=-1) t[right].tag3=(t[right].tag3+t[k].tag1)%N; else { pushdown(right); t[right].tag1=(t[right].tag1+t[k].tag1)%N; } t[k].tag1=0;//记得还原 }}void update(int l,int r,int v,int d,int k){ if(t[k].l==l&&t[k].r==r) { if(d==1) { if(t[k].tag3!=-1)//如果有等号标记,就直接修改等号标记 { t[k].tag3=(t[k].tag3+v%N)%N; } else { pushdown(k);//否则清空该区间,进行标记 t[k].tag1=(t[k].tag1+v%N)%N; } } else if(d==2)//同理 { if(t[k].tag3!=-1) { t[k].tag3=(t[k].tag3*v%N)%N; } else { pushdown(k); t[k].tag2=(t[k].tag2*v%N)%N; } } else { t[k].tag3=v%N; t[k].tag1=0; t[k].tag2=1; } return; } pushdown(k);//向下更新 int mid=(t[k].l+t[k].r)/2; if(r<=mid) update(l,r,v,d,left); else if(l>mid) update(l,r,v,d,right); else { update(l,mid,v,d,left); update(mid+1,r,v,d,right); }}int query(int l,int r,int p,int k)//查询{ if(t[k].l>=l&&t[k].r<=r&&t[k].tag3!=-1)//查到是查询区间的子区间且一段全为相同的数 { int temp=1; for(int i=1;i<=p;i++) temp=(temp*t[k].tag3)%N; return ((t[k].r-t[k].l+1)%N*temp)%N;//注意要乘上长度 } pushdown(k); int mid=(t[k].l+t[k].r)/2; if(r<=mid) return query(l,r,p,left)%N; else if(l>mid) return query(l,r,p,right)%N; else { return (query(l,mid,p,left)+query(mid+1,r,p,right))%N; }}int main(){ int i,j,k,m,d,x,y,c; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0) break; Build(1,n,1); for(i=0;i<m;i++) { scanf("%d%d%d%d",&d,&x,&y,&c); if(d>=1&&d<=3) { update(x,y,c,d,1); } else { printf("%d\n",query(x,y,c,1)%N); } } } return 0;}
- HDU 4578 Transformation(线段树+做了4天的神题+详解)
- HDU 4578 Transformation --线段树,好题
- hdu 4578 Transformation 线段树
- HDU 4578 Transformation (线段树)
- hdu-4578-Transformation-线段树
- HDU 4578 Transformation 线段树
- HDU 4578 Transformation[线段树]
- hdu 4578 Transformation (线段树)
- hdu 4578 Transformation 线段树
- hdu 4578 Transformation 线段树
- HDU 4578 Transformation 线段树
- HDU 4578 Transformation(最恶心的线段树)
- hdu 4578 Transformation(区间线段树)
- hdu 4578 Transformation(线段树)
- hdu 4578 Transformation [线段树 区间更新]
- HDU - 4578 Transformation(线段树 区间修改)
- HDU 4578-Transformation(线段树)
- HDU - 4578 -Transformation(线段树)
- 快速幂,矩阵快速幂(模板)
- nmcli 网卡链路绑定team
- CSU 1569Wet Tiles
- 最少拦截系统
- hdu 1757 A Simple Math Problem(矩阵快速幂基础题)
- HDU 4578 Transformation(线段树+做了4天的神题+详解)
- Ubuntu的Samba服务,实现Windows映射网络盘到Ubuntu
- python 关于协程的二三事
- linux服务器查看防火墙状态
- laravel资源路由
- mysql使用临时量排序
- 2017多校1 1006Function
- python os.path 骚操作
- What is a NullPointerException?