HDU - 6183 暴力,线段树动态开点,cdq分治
B - Color itHDU - 6183
题目大意:有三种操作,0是清空所有点,1是给点(x,y)涂上颜色c,2是查询满足1<=a<=x,y1<=b<=y2的(a,b)点一共有几种不同的颜色
一开始做的时候直接就是开51个vector保存每个颜色相应的点,然后就是询问就是,暴力循环判断这个颜色存不存在一个满足条件的点,感觉最差情况下应该会超时,不过却过了
1 #include<cstdio> 2 #include<vector> 3 using namespace std; 4 struct Node{ 5 int x,y; 6 Node(){} 7 Node(int x,int y):x(x),y(y){} 8 }; 9 vector<Node> c[58]; 10 int main() 11 { 12 int op,x,y,y1,cc; 13 while(scanf("%d",&op)&&op!=3) 14 { 15 if(op==0) 16 { 17 for(int i=0;i<=50;i++) 18 c[i].clear(); 19 } 20 else if(op==1) 21 { 22 scanf("%d%d%d",&x,&y,&cc); 23 c[cc].push_back(Node(x,y)); 24 } 25 else 26 { 27 scanf("%d%d%d",&x,&y,&y1); 28 int ans=0; 29 for(int i=0;i<=50;i++) 30 { 31 for(int j=0;j<c[i].size();j++) 32 if(c[i][j].x<=x&&c[i][j].y>=y&&c[i][j].y<=y1) 33 { 34 ans++; 35 break; 36 } 37 } 38 printf("%d\n",ans); 39 } 40 } 41 return 0; 42 }
暴力过一切
然后看网上有是线段树动态开点的做法,但实际上并不比上面暴力的写法快,反而慢上几十ms,不过可以当做一个算法扩展来联系。
首先1操作肯定就是单点更新了,而2操作上已经限定了x的左边为1,所以我们以y轴来建线段树维护个区间内x的最小值,那么2操作就是区间查询了。但我们知道正常静态的线段树需要4*SIZE的节点空间来保存信息的,在这里又需要51颗线段树,也就是51*4*1000000的空间来保存节点信息,不知道你们电脑能不能开那么大的数组,反正我的电脑和OJ的虚拟机是不行的。但其实最多150000个1操作和2操作,并不需要那么大的空间。所以这时候需要用到线段树的动态开点了。
静态的线段树,每个编号为x的节点,它的左孩子编号就为2*x,右孩子编号就为2*x+1,然后这个节点x,我们是保存它的区间L,R和其他一系列信息。而动态开点的话,对于编号为x的节点,它的左右孩子的编号就不一定是2*x和2*x+1的关系了,所以我们要保存下的是它的左右孩子的编号已经一系列相关的信息,然后对于每个节点就是当需要到它时再开辟它。其他操作就和静态的线段树差不多。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N=1001108; 5 struct Tree{ 6 int lson,rson,minx; 7 }T[N]; 8 int tn,flag,root[52]; 9 //root就保存这个颜色对于的那棵线段树的根节点编号 10 void init() 11 { 12 tn=1; 13 for(int i=0;i<=50;i++) 14 root[i]=0; 15 } 16 void updata(int &id,int L,int R,int y,int x)//在这里用L,R来表示节点id的区间 17 { 18 if(!id) 19 {//需要到这个节点了,开辟这个节点 20 id=tn++; 21 T[id].minx=N; 22 T[id].lson=T[id].rson=0;//它的子节点还未用得上 23 } 24 T[id].minx=min(T[id].minx,x);//更新最小的x值 25 //下面就是线段树的单点修改 26 if(L==R) 27 return ; 28 int mid=(L+R)>>1; 29 if(y<=mid) 30 updata(T[id].lson,L,mid,y,x); 31 else 32 updata(T[id].rson,mid+1,R,y,x); 33 } 34 void query(int id,int L,int R,int l,int r,int x) 35 { 36 if(flag||!id)//如果已经有点满足条件,或者这个节点没开辟就返回 37 return ; 38 if(l<=L&&r>=R) 39 { 40 if(T[id].minx<=x) 41 flag=1;//在y1和y2范围内有个x满足条件 42 return ; 43 } 44 int mid=(L+R)>>1; 45 if(l<=mid) 46 query(T[id].lson,L,mid,l,r,x); 47 if(r>mid) 48 query(T[id].rson,mid+1,R,l,r,x); 49 } 50 int main() 51 { 52 int op,x,y,y2,c; 53 init(); 54 while(~scanf("%d",&op)&&op!=3) 55 { 56 if(op==0) 57 init(); 58 else if(op==1) 59 { 60 scanf("%d%d%d",&x,&y,&c); 61 updata(root[c],1,1000000,y,x); 62 } 63 else 64 { 65 scanf("%d%d%d",&x,&y,&y2); 66 int ans=0; 67 for(int i=0;i<=50;i++) 68 { 69 flag=0; 70 query(root[i],1,1000000,y,y2,x); 71 ans+=flag; 72 } 73 printf("%d\n",ans); 74 } 75 } 76 return 0; 77 }
线段树下线段果
正解是cdq分治,待我学成归来,再更新。。。我回来了
cdq分治处理的话,就是三维偏序的一个处理,(操作时间,x轴,y轴),然后第一维已经有序,那么我们cdq分治处理第二维,然后线段树处理第三维。因为最多是50种颜色,那么我们采用状压的策略,把每个颜色C用2C来表示,然后线段树维护个区间或和。需要注意的就是y轴离散化下,不然容易超时。
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 #define L(x) (x<<1) 5 #define R(x) (x<<1|1) 6 #define M(x) ((T[x].l+T[x].r)>>1) 7 using namespace std; 8 typedef long long ll; 9 const int N=150118,Y=1000118; 10 struct Tree{ 11 int l,r; 12 ll val; 13 }T[Y]; 14 struct Nop{ 15 int op,x,y,y1; 16 ll val;//op1更新操作的val记录2^C,op2查询操作记录答案编号 17 friend bool operator <(const Nop &n1,const Nop &n2){ 18 return n1.x==n2.x ? n1.op<n2.op : n1.x<n2.x; 19 } 20 }P[N<<1],temp[N<<1]; 21 int pn=0,qn=0,newy[Y]; 22 bool num[Y]={0}; 23 vector<int> vy; 24 ll ans[N]={0},cf2[52]={1}; 25 inline void addp(int op,int x,int y,int y1,ll val){ 26 P[pn++]=(Nop){op,x,y,y1,val}; 27 } 28 inline void addy(int y) 29 { 30 if(!num[y]) 31 { 32 num[y]=1; 33 vy.push_back(y); 34 } 35 } 36 void built(int id,int l,int r) 37 { 38 T[id].val=0; 39 T[id].l=l,T[id].r=r; 40 if(l==r) 41 return ; 42 built(L(id),l,M(id)); 43 built(R(id),M(id)+1,r); 44 } 45 //线段树单点修改 46 void updata(int id,int pos,ll val) 47 { 48 if(T[id].l==T[id].r&&T[id].l==pos) 49 { 50 if(val) 51 T[id].val|=val; 52 else 53 T[id].val=0; 54 return ; 55 } 56 if(pos<=M(id)) 57 updata(L(id),pos,val); 58 else 59 updata(R(id),pos,val); 60 T[id].val=T[L(id)].val|T[R(id)].val; 61 } 62 //区间或和查询 63 ll query(int id,int l,int r) 64 { 65 ll ans=0; 66 if(l<=T[id].l&&T[id].r<=r) 67 return T[id].val; 68 if(l<=M(id)) 69 ans|=query(L(id),l,r); 70 if(r>M(id)) 71 ans|=query(R(id),l,r); 72 return ans; 73 } 74 void cdq(int l,int r) 75 { 76 if(l==r) 77 return ; 78 int m=(l+r)>>1; 79 cdq(l,m); 80 cdq(m+1,r); 81 int i=l,j=m+1,k=l; 82 while(i<=m&&j<=r) 83 { 84 if(P[i]<P[j]) 85 { 86 if(P[i].op==1) 87 updata(1,P[i].y,P[i].val); 88 temp[k++]=P[i++]; 89 } 90 else 91 { 92 if(P[j].op==2) 93 ans[P[j].val]|=query(1,P[j].y,P[j].y1); 94 temp[k++]=P[j++]; 95 } 96 } 97 while(i<=m) 98 temp[k++]=P[i++]; 99 while(j<=r) 100 { 101 if(P[j].op==2) 102 ans[P[j].val]|=query(1,P[j].y,P[j].y1); 103 temp[k++]=P[j++]; 104 } 105 for(i=l;i<=r;i++) 106 { 107 if(P[i].op==1) 108 updata(1,P[i].y,0); 109 P[i]=temp[i]; 110 } 111 } 112 void solve() 113 { 114 if(pn) 115 { 116 //离散化部分 117 sort(vy.begin(),vy.end()); 118 for(int i=0;i<vy.size();i++) 119 { 120 newy[vy[i]]=i+1; 121 num[vy[i]]=0; 122 } 123 for(int i=0;i<pn;i++) 124 { 125 if(P[i].op==1) 126 P[i].y=newy[P[i].y]; 127 else 128 P[i].y=newy[P[i].y],P[i].y1=newy[P[i].y1]; 129 } 130 //进行分治 131 cdq(0,pn-1); 132 } 133 for(int i=0;i<qn;i++) 134 { 135 int sum=0; 136 //判断2^0+2^1+2^2+...+2^50含有哪些 137 for(int j=0;j<=50;j++) 138 if(ans[i]&cf2[j]) 139 sum++; 140 printf("%d\n",sum); 141 ans[i]=0; 142 } 143 pn=qn=0; 144 vy.clear(); 145 } 146 int main() 147 { 148 int op,x,y,c,y1; 149 built(1,1,N); 150 for(int i=1;i<=50;i++) 151 cf2[i]=cf2[i-1]<<1; 152 while(~scanf("%d",&op)&&op!=3) 153 { 154 if(op==0) 155 solve(); 156 else if(op==1) 157 { 158 scanf("%d%d%d",&x,&y,&c); 159 addp(1,x,y,0,cf2[c]); 160 addy(y); 161 } 162 else 163 { 164 scanf("%d%d%d",&x,&y,&y1); 165 addp(2,x,y,y1,qn++); 166 addy(y); 167 addy(y1); 168 } 169 } 170 solve(); 171 return 0; 172 }
一分二二分四
但实际上,3个实现方法中,暴力最快。。。数据太坑了
转载于:https://www.cnblogs.com/LMCC1108/p/10699522.html
HDU - 6183 暴力,线段树动态开点,cdq分治相关推荐
- 线段树动态开点区间加区间求和
线段树动态开点区间加区间求和 题目来源: 陕西师范大学第七届程序设计竞赛网络同步赛 H. 万恶的柯怡 思想: 保证叶子节点被完整的覆盖,需要开节点,就把左右儿子都开出来,其余和普通线段树一样. tip ...
- ZYH的斐波那契数列【线段树动态开点+矩阵快速幂求斐波那契】
描述 ZYH最近研究数列研究得入迷啦! 现在有一个斐波拉契数列(f[1]=f[2]=1,对于n>2有f[n]=f[n-1]+f[n-2]), 但是斐波拉契数列太简单啦,于是ZYH把它改成了斐波拉 ...
- 【题解】有便便的厕所(权值线段树动态开点模板题)
我只是来填坑的. 关于权值线段树的更多有关内容见此. 题面 题目描述 众所周知, GM \texttt{GM} GM 家的狗特别喜欢拉便便. GM \texttt{GM} GM 为了方便它方便,在家里 ...
- HDU - 5709 Claris Loves Painting 线段树动态开点+合并
题目链接:https://cn.vjudge.net/problem/HDU-5709 题意:给定一棵n点的树,每个节点上有一个颜色,每次询问一个点的子树中与这个点距离不超过d的点的颜色有多少种 题解 ...
- BZOJ3531-[Sdoi2014]旅行(树剖+线段树动态开点)
传送门 完了今天才知道原来线段树的动态开点和主席树是不一样的啊 我们先考虑没有宗教信仰的限制,那么就是一个很明显的树剖+线段树,路径查询最大值以及路径和 然后有了宗教信仰的限制该怎么做呢? 先考虑暴力 ...
- Codeforces Round #737 (Div. 2) D. Ezzat and Grid 线段树动态开点
传送门 文章目录 题意: 思路: 题意: 思路: 比较套路的一个题,我们维护一个dp[i]dp[i]dp[i]表示到了第iii行能保留的区间最多是多少. 转移比较明显:dp[i]=max(dp[j]) ...
- 线段树动态开点 - - - > 线段树合并
逆序对 代码 P3224 [HNOI2012]永无乡 并查集+线段树合并 代码 P5494 [模板]线段树分裂 #include<iostream> #include ...
- 权值线段树+动态开点(学习小结)
首先先上一道板题:把它看懂就OK了,其实这个跟普通的线段树也没太大区别,就是存的是出现的次数. CODE #include<algorithm> #include<cstdio> ...
- HDU 2795 Billboard (线段树+贪心)
HDU 2795 Billboard (线段树+贪心) 手动博客搬家:本文发表于20170822 21:30:17, 原地址https://blog.csdn.net/suncongbo/articl ...
最新文章
- P1119 灾后重建(经典floyd)
- Apache Ivy
- 面对万物互联的智能世界,你是否也想分一杯羹
- 键盘出现与消失的监听方法
- 06-02 Jenkins job 机制
- webpack文件系统缓存
- 实验4-1-2 求奇数和 (15 分)
- 拓端tecdat|R语言文本挖掘使用tf-idf分析NASA元数据的关键字
- 6 使用soap客户端_SOAP技术应用总结
- Python100道经典练习题(附PDF)
- 线性代数 --- 线性代数中的一些特殊矩阵(被广泛用于高斯消元法的消元矩阵E)(个人笔记扫描版)
- PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection阅读
- keepalived配置文件相关简单解释
- 程序员必读: 摸清Hash表的脾性
- linux 卸载windows系统服务,如何卸载windows系统
- Scrapy 第一次爬虫
- 数据恢复原理与数据清除原理
- 坚持#第369天~知道了惠普打印机和佳能打印机打印不清晰了怎么解决
- 网络能力认证CCSC-管理1级 技术1级别
- python安装lap_AP 升级到 LAP,或 LAP 降到 AP 的方法