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分治,待我学成归来,再更新。。。

猜你喜欢

转载自www.cnblogs.com/LMCC1108/p/10699522.html