第一次听到离散化是今年省赛的时候,一道矩形并的题,很水,就两个矩形...
今天再去做线段树已经发现离散化忘得差不多了...水逼的悲哀啊...
先看简单点的hdu 1698
http://acm.hdu.edu.cn/showproblem.php?pid=1698
先做这个水题,在做poj 2528,当然poj 2528也很水
一、建树
把hook作为线段建树,近乎直接套线段树的模板。
二、计算结果
void cal(int i) { if(node[i].v) { tot+=(node[i].r-node[i].l+1)*node[i].v; } else { cal(i*2); cal(i*2+1); } }
//poj 2528算的时候也是这样,近乎模板啊
#include<cstdio> #include<cstring> #include<cstdlib> using namespace std; #define N 100005 struct Node{ int l,r,m,v; }; Node node[N*3]; int tot; void build(int i,int left,int right) { Node &tmp=node[i]; tmp.l=left; tmp.r=right; tmp.m=(left+right)>>1; tmp.v=1; if(left<right) { build(i*2,left,tmp.m); build(i*2+1,tmp.m+1,right); } } void update(int i,int left,int right,int v) { Node &tmp=node[i]; if(tmp.l==left&&tmp.r==right) { tmp.v=v; return ; } if(tmp.v==v)return ;/*有这一句可以省一些时间*/ if(node[i].v>0) { node[i*2].v=node[i*2+1].v=node[i].v;/*存储父亲节点的值*/ node[i].v=0; } /*downside >= or <= think about m,m+1*/ if(right<=tmp.m) update(i*2,left,right,v); else if(tmp.m<left) update(i*2+1,left,right,v); else { update(i*2,left,tmp.m,v); update(i*2+1,tmp.m+1,right,v); } } void cal(int i) { if(node[i].v) { tot+=(node[i].r-node[i].l+1)*node[i].v; } else { cal(i*2); cal(i*2+1); } } int main() { int ncase,k,n,q,i,l,r,v; //freopen("hdu 1698.in","r",stdin); scanf("%d",&ncase); for(k=1;k<=ncase;k++) { scanf("%d%d",&n,&q); build(1,1,n); for(i=0;i<q;i++) { scanf("%d%d%d",&l,&r,&v); update(1,l,r,v); } tot=0; cal(1); printf("Case %d: The total value of the hook is %d.\n",k,tot); } return 0; }
谈谈离散化,举个例子,1-5,3-9,11-18三条线段,此题根本不需要线段长度,
所以直接当成
1->1
3->2;
5->3;
9->4;
11->5
18->6;
于是原来的线段就转化为 1->3,2->4,5->6.这样建树会节省空间
另外建树的时候有个问题,tree[N*3]会RE,要tree[N*4]
其他跟poj 2528 一样的,关于离散化的具体做法,我在注释里写清楚吧,下面贴代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> using namespace std; #define N 20002 struct node{ int l,r,c; }; node tree[N*4]; struct line{ int s,id; }; line seg[N]; int des[N][2],ans,numc[N]; bool cmp(line a,line b) { return a.s<b.s; } void build(int i,int left,int right) { tree[i].l=left; tree[i].r=right; tree[i].c=0; if(left!=right) { int mid=(left+right)>>1; build(i*2,left,mid); build(i*2+1,mid+1,right); } } void update(int i,int ll,int rr,int col) { if(tree[i].l==ll&&tree[i].r==rr) { tree[i].c=col; return; } if(tree[i].c==col)return; int mid=(tree[i].l+tree[i].r)>>1; if(tree[i].c>0) { tree[i*2].c=tree[i*2+1].c=tree[i].c; tree[i].c=0; } if(rr<=mid) update(i*2,ll,rr,col); else if(mid<ll) update(i*2+1,ll,rr,col); else { update(i*2,ll,mid,col); update(i*2+1,mid+1,rr,col); } } void cal(int i) { if(tree[i].c>0) { if(!numc[tree[i].c]) { numc[tree[i].c]=1; ans++; } } else { cal(i*2); cal(i*2+1); } } int main() { //freopen("in.txt","r",stdin); int i,j,ncase,n; scanf("%d",&ncase); while(ncase--) { scanf("%d",&n); for(i=0;i<n;i++) { scanf("%d%d",&des[i][0],&des[i][1]); seg[i*2].s=des[i][0]; seg[i*2].id=-(i+1); seg[i*2+1].s=des[i][1]; seg[i*2+1].id=i+1; /* 这里解释一下,为什么是seg[i*2],seg[i*2+1]? des数组起到两个作用: 一是记录原来的线段区间,就是这里的 scanf("%d%d",&des[i][0],&des[i][1]); 二是记录转化后的线段区间,就是下面num记点的个数 */ } sort(seg,seg+n*2,cmp); /*以下为离散化*/ int tmp=seg[0].s,num=1; for(i=0;i<n*2;i++) { if(seg[i].s!=tmp) { num++; tmp=seg[i].s; } if(seg[i].id<0) des[-seg[i].id-1][0]=num; else des[seg[i].id-1][1]=num; } /*建树及更新*/ build(1,1,num); int color=1; for(i=0;i<n;i++) update(1,des[i][0],des[i][1],color++); ans=0; memset(numc,0,sizeof(numc)); cal(1); printf("%d\n",ans); } return 0; }