线段树—倒推优化

来源:互联网 发布:酒吧调音软件 编辑:程序博客网 时间:2024/06/14 04:16

涂色问题有一个特点:后面的往往会覆盖前面的。所以如果我们顺推的话,更改次数会很多;如果考虑倒推,那么每个点只需要更改一次,即总更改次数为n。


例题:(pku 2528)

【题目描述】有L(L没有给出)段线段(编号为1~L) 1~1000 0000(数组无法定义几千万,所以要用离散化),有 M次操作(1 <= M <= 1 0000),每次操作都是给出lr,第i次操作的意义就是:把第l段到第r段的线段染色为颜色i

【输入格式】第一行给出t,表示有t组数据。每组数据给出M表示下来有M个操作。下来M个操作,每行两个整数(l,r)表示把第l段到第r段的线段染色为颜色i注意后面的染的颜色会覆盖前面的染的颜色。

【输出格式】问最后可以看到的有多少种不一样的颜色。


#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct node1{int l,r,lc,rc,c;}tr[80010];int len;//c=0 可贴海报;c>0 已有海报 struct node2{int x,p,z;}a[20010],b[20010];bool vk,v[10010];int cmp(node2 p1,node2 p2){return p1.x<p2.x;}void bt(int l,int r){len++;int now=len;tr[now].l=l;tr[now].r=r;tr[now].lc=tr[now].rc=-1;tr[now].c=0;if(l<r){int mid=(l+r)/2;tr[now].lc=len+1; bt(l,mid);tr[now].rc=len+1; bt(mid+1,r);}}void change(int now,int l,int r){if(tr[now].c>0) return ;if(tr[now].l==l&&tr[now].r==r&&tr[now].c==0){tr[now].c=1;vk=true;return ;}if(tr[now].l+1==tr[now].r) return ;int mid=(tr[now].l+tr[now].r)/2;int lc=tr[now].lc,rc=tr[now].rc;if(tr[now].c>=0) tr[lc].c=tr[rc].c=tr[now].c;if(r<=mid) change(lc,l,r);else if(mid+1<=l) change(rc,l,r);else{change(lc,l,mid);change(rc,mid+1,r);}if(tr[lc].c==tr[rc].c) tr[now].c=tr[lc].c;else tr[now].c=-1;}int main(){int T;scanf("%d",&T);while(T--){int n;scanf("%d",&n);n*=2;for(int i=1;i<=n;i+=2){scanf("%d%d",&a[i].x,&a[i+1].x);a[i].p=i;a[i+1].p=i+1;}for(int i=1;i<=n;i++) b[i]=a[i];sort(b+1,b+n+1,cmp);b[1].z=1;for(int i=2;i<=n;i++){if(b[i].x==b[i-1].x) b[i].z=b[i-1].z;else if(b[i].x-b[i-1].x==1) b[i].z=b[i-1].z+1;else b[i].z=b[i-1].z+2;}for(int i=1;i<=n;i++) a[b[i].p].z=b[i].z;len=0;bt(1,b[n].z);memset(v,false,sizeof(v));for(int i=n-1;i>=1;i-=2)//倒推:从n~1 {vk=false;change(1,a[i].z,a[i+1].z);if(vk==true) v[(i+1)/2]=true;}int ans=0;for(int i=1;i<=n/2;i++){if(v[i]==true) ans++;}printf("%d\n",ans);}return 0;}


0 0
原创粉丝点击