[kuangbin带你飞]专题七 线段树
228 / 440 Problem A HDU 1166 敌兵布阵
hint.解题报告见之前博客
207 / 438 Problem B HDU 1754 I Hate It
d.给出N个学生的成绩,现在有2种操作,
操作1:询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
操作2:把ID为A的学生的成绩更改为B。
对于每个询问,输出最高成绩。
s.线段树单点更新,区间查询。
#include<iostream> #include<stdio.h> using namespace std; #define MAXN 200005 int ans; int grade[MAXN]; struct node{ int left,right;//,sum; int ma;//区间最大值 int mid(){ return (left+right)>>1; } }tree[MAXN*4];//注意范围,4倍空间 void btree(int left,int right,int rt){//建树 tree[rt].left=left; tree[rt].right=right; if(left==right){ //scanf("%d",&tree[rt].sum); tree[rt].ma=grade[left]; return; } int mid=tree[rt].mid(); btree(left,mid,rt<<1); btree(mid+1,right,rt<<1|1); //tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;//区间里的点数=左区间+右区间 tree[rt].ma=tree[rt<<1].ma>tree[rt<<1|1].ma?tree[rt<<1].ma:tree[rt<<1|1].ma; } void query(int left,int right,int rt,int L,int R){//询问求最大值 if(L<=left&&right<=R){ //ans+=tree[rt].sum; if(tree[rt].ma>ans){ ans=tree[rt].ma; } return; } int mid=tree[rt].mid(); if(R<=mid)query(left,mid,rt<<1,L,R);//区间在左子树 else if(L>mid)query(mid+1,right,rt<<1|1,L,R);//区间在右子树 else{ query(left,mid,rt<<1,L,R); query(mid+1,right,rt<<1|1,L,R); } } void update(int left,int right,int rt,int pos,int add){//单点更新函数 if(left==right){ //tree[rt].sum+=add; tree[rt].ma=add; return; } int mid=tree[rt].mid(); if(pos<=mid)update(left,mid,rt<<1,pos,add);//点在左子树 else update(mid+1,right,rt<<1|1,pos,add);//点在右子树 //tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;//区间和更新 tree[rt].ma=tree[rt<<1].ma>tree[rt<<1|1].ma?tree[rt<<1].ma:tree[rt<<1|1].ma;//区间最大值更新 } int main(){ int N,M; char str[2]; int A,B; while(~scanf("%d%d",&N,&M)){ for(int i=1;i<=N;++i){ scanf("%d",&grade[i]); } btree(1,N,1); for(int i=0;i<M;++i){ scanf("%1s",str); scanf("%d%d",&A,&B); if(str[0]=='Q'){ ans=0; query(1,N,1,A,B); printf("%d\n",ans); } else{ update(1,N,1,A,B); } } } return 0; }
181 / 727 Problem C POJ 3468 A Simple Problem with Integers
hint.解题报告见之前博客
105 / 410 Problem D POJ 2528 Mayor's posters
d.n张海报,高度相同,给出要贴在墙上的左右端点,后面贴的覆盖前面的,求最后能看到的海报数。
s.宽度范围很大,离散化
c.从前向后贴
/* 线段树 区间更新 */ #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define L(root) ((root)<<1) #define R(root) (((root)<<1)|1) const int MAXN=10005;// int numbers[MAXN];//初始值 int myHash[10000005]; bool vis[MAXN];//这种颜色存在标志 struct node{ int left,right;// int color; int flag;//-1不向下更新,1向下更新,即单色时 int mid(){ return left+((right-left)>>1); } }tree[MAXN*2*4];//4倍空间 void pushUp(int root){ } void pushDown(int root){ if(tree[root].flag==1){//需要向下更新 tree[L(root)].flag=1; tree[R(root)].flag=1; tree[L(root)].color=tree[root].color; tree[R(root)].color=tree[root].color; tree[root].flag=-1; } } void build(int root,int left,int right){ tree[root].left=left; tree[root].right=right; tree[root].flag=-1; if(left==right){ return; } int mid=tree[root].mid(); build(L(root),left,mid); build(R(root),mid+1,right); } int query(int root,int left,int right){ if(tree[root].flag==1){//当前区间为单色的 if(!vis[tree[root].color]){//第一次出现这种颜色 vis[tree[root].color]=true; return 1; } return 0; } int mid=tree[root].mid(); if(right<=mid){ return query(L(root),left,right); } else if(left>mid){ return query(R(root),left,right); } else{ return query(L(root),left,mid)+query(R(root),mid+1,right); } } void update(int root,int left,int right,int add){ if(tree[root].flag==1&&tree[root].color==add){ return;//小剪枝,当前区间单色且与要更改的区间颜色相同则返回不用深入 } if(tree[root].left==left&&tree[root].right==right){ tree[root].color=add; tree[root].flag=1; return; } pushDown(root); int mid=tree[root].mid(); if(right<=mid){ update(L(root),left,right,add); } else if(left>mid){ update(R(root),left,right,add); } else{ update(L(root),left,mid,add); update(R(root),mid+1,right,add); } } int main(){ int c; int n; int post[MAXN][2];//每个区间 int p[MAXN*2];//所有的点 int cnt;//所有点的个数 int p2[MAXN*2];//去除重复 int cnt2; int i; scanf("%d",&c); while(c--){ scanf("%d",&n); cnt=0; for(i=0;i<n;++i){ scanf("%d%d",&post[i][0],&post[i][1]); p[cnt++]=post[i][0];//保存所有端点 p[cnt++]=post[i][1]; } sort(p,p+cnt);//排序 p2[0]=p[0]; cnt2=1; for(i=1;i<cnt;++i){//去重 if(p[i]!=p[i-1]){ p2[cnt2++]=p[i]; } } for(i=0;i<cnt2;++i){//所有点hash映射到0~cnt2-1 myHash[p2[i]]=i; } build(1,0,cnt2-1);//0~cnt2-1 for(i=0;i<n;++i){//n种颜色从头开始涂 update(1,myHash[post[i][0]],myHash[post[i][1]],i); } memset(vis,false,sizeof(vis)); printf("%d\n",query(1,0,cnt2-1));//0~cnt2-1 } return 0; }
c2.从后向前看
/* HDU 2528 Mayor's posters 本题大意:给定一些海报,可能相互重叠,告诉你每个海报 的宽度(高度都一样的)和先后叠放顺序,问没有被完全盖住的有多少张? 海报最多10000张,但是墙有10000000块瓷砖长,海报不会落在瓷砖中间。 如果直接建树,就算不TLE,也会MLE。即单位区间长度太多。 其实10000张海报,有20000个点,最多有19999个区间。对各个区间编号,就是离散化。然后建数。 其实浮点数也是一样离散化的。 writer:kuangbin */ #include<stdio.h> #include<algorithm> #include<math.h> using namespace std; const int MAXN=10010; struct Cpost { int l,r; } posters[MAXN]; int x[MAXN*2]; int hash[10000005]; struct Node { int l,r; bool bCovered;//标记是否被完全覆盖 } segTree[MAXN*8]; //这里必须开到线段数的四倍,?? void Build(int i,int l,int r)//建立线段树 { segTree[i].l=l; segTree[i].r=r; segTree[i].bCovered=false; if(l==r)return; int mid=(l+r)>>1; Build(i<<1,l,mid); Build(i<<1|1,mid+1,r); } bool Post(int i,int l,int r)//贴上一个好报,同时判断是否被完全覆盖 { if(segTree[i].bCovered) return false; if(segTree[i].l==l&&segTree[i].r==r) { segTree[i].bCovered=true; return true; } bool bResult; int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) bResult=Post(i<<1,l,r); else if(l>mid) bResult=Post(i<<1|1,l,r); else { bool b1=Post(i<<1,l,mid); bool b2=Post(i<<1|1,mid+1,r); bResult=b1||b2;//不能直接或上去,因为如果前面的真,后面的会不做的 } //这个很重要,要反馈回原结点,如果左右儿子都被完全覆盖了,自然也完全覆盖了 if(segTree[i<<1].bCovered && segTree[i<<1|1].bCovered) segTree[i].bCovered=true; return bResult; } int main() { int T; int i,j,k; int n; scanf("%d",&T); while(T--) { scanf("%d",&n); int nCount=0; for(i=0; i<n; i++) { scanf("%d%d",&posters[i].l,&posters[i].r); x[nCount++]=posters[i].l; x[nCount++]=posters[i].r; } sort(x,x+nCount);//先排序 nCount=unique(x,x+nCount)-x;//合并掉相同的项 for(i=0; i<nCount; i++) hash[x[i]]=i; Build(1,0,nCount-1); int res=0; for(i=n-1; i>=0; i--) //要从上面开始看。 if(Post(1,hash[posters[i].l],hash[posters[i].r])) res++; printf("%d\n",res); } return 0; }
138 / 230 Problem E HDU 1698 Just a Hook
d.刚开始区间内元素全是1,每次操作可以把一个区间里的值全都变为1或2或3,求经过一些操作后,整个区间的元素的和
s.区间更新,区间查询。
#include<iostream> #include<stdio.h> using namespace std; #define L(root) ((root) << 1) #define R(root) (((root) << 1) + 1) const int MAXN = 100005; //int numbers[MAXN]; struct st { // 区间范围 int left, right; // 更新值、区间总和 long long delta, sum; } st[MAXN * 4]; // 建树代码基本不变 void build(int root, int l, int r) { st[root].left = l, st[root].right = r, st[root].delta = 0; if (l == r) { //st[root].sum = numbers[l]; st[root].sum=1; return; } int m = l + ((r - l) >> 1); build(L(root), l, m); build(R(root), m + 1, r); st[root].sum = st[L(root)].sum + st[R(root)].sum; } long long query(int root, int l, int r) { // 如查询区间恰等于节点区间,直接返回该区间总和即可 if (st[root].left == l && st[root].right == r) { return st[root].sum; } // 否则需将当前区间的“缓冲”值更新下去并修正各节点区间的总和 if (st[root].delta) { st[L(root)].delta = st[root].delta; st[R(root)].delta = st[root].delta; st[L(root)].sum = st[root].delta * (st[L(root)].right - st[L(root)].left + 1); st[R(root)].sum = st[root].delta * (st[R(root)].right - st[R(root)].left + 1); st[root].delta = 0; } int m = st[root].left + ((st[root].right - st[root].left) >> 1); if (r <= m) { return query(L(root), l, r); } else if (l > m) { return query(R(root), l, r); } else { return query(L(root), l, m) + query(R(root), m + 1, r); } } void update(int root, int l, int r, long long v) { // 如变更区间恰等于节点区间,只修正当前节点区间即可 if (st[root].left == l && st[root].right == r) { st[root].delta = v; st[root].sum = v * (r - l + 1); return; } // 否则需向下修正相关节点区间 if (st[root].delta) { st[L(root)].delta = st[root].delta; st[R(root)].delta = st[root].delta; st[L(root)].sum = st[root].delta * (st[L(root)].right - st[L(root)].left + 1); st[R(root)].sum = st[root].delta * (st[R(root)].right - st[R(root)].left + 1); st[root].delta = 0; } int m = st[root].left + ((st[root].right - st[root].left) >> 1); if (r <= m) { update(L(root), l, r, v); } else if (l > m) { update(R(root), l, r, v); } else { update(L(root), l, m, v); update(R(root), m + 1, r, v); } // 同时一定要记得修正当前节点区间的总和 st[root].sum = st[L(root)].sum + st[R(root)].sum; } int main() { int T,N,Q; int X,Y,Z; __int64 ans; int C=0; scanf("%d",&T); while(T--){ scanf("%d",&N); build(1,1,N); scanf("%d",&Q); for(int i=0;i<Q;++i){ scanf("%d%d%d",&X,&Y,&Z); update(1,X,Y,Z); } ans=query(1,1,N); printf("Case %d: The total value of the hook is %I64d.\n",++C,ans); } return 0; }
c2.一个map写的代码,思想不错。不过最坏是O(MQ)
#include<stdio.h> const int n = 1000005; int map[n][3]; int main() { int i,M,z,N,sum,v; scanf("%d",&N); for(i=1; i<=N; i++) { sum = 0; int Q,j; scanf("%d%d",&M,&Q); for(j=1; j<=Q; j++) scanf("%d%d%d",&map[j][0],&map[j][1],&map[j][2]); for(j=1; j<=M; j++) { v = 1; for(int k=Q; k>=0; k--)//从末尾开始 即从更新的最后一次算起 if(map[k][0]<=j && j<=map[k][1]) { v = map[k][2]; break;//覆盖便弹出 } sum += v; } printf("Case %d: The total value of the hook is %d.\n",i,sum); } }
105 / 305 Problem F ZOJ 1610 Count the Colors
hint.解题报告见之前博客
120 / 176 Problem G POJ 3264 Balanced Lineup
d.区间最大值最小值的差
/* POJ 3264 Balanced Lineup 题目意思:给定Q(1<=Q<=200000)个数A1,A2,```,AQ, 多次求任一区间Ai-Aj中最大数和最小数的差 */ #include<stdio.h> #include<algorithm> #include<iostream> using namespace std; #define MAXN 200005 #define INF 10000000 int nMax,nMin;//记录最大最小值 struct Node { int l,r;//区间的左右端点 int nMin,nMax;//区间的最小值和最大值 }segTree[MAXN*3]; int a[MAXN]; void Build(int i,int l,int r)//在结点i上建立区间为(l,r) { segTree[i].l=l; segTree[i].r=r; if(l==r)//叶子结点 { segTree[i].nMin=segTree[i].nMax=a[l]; return; } int mid=(l+r)>>1; Build(i<<1,l,mid); Build(i<<1|1,mid+1,r); segTree[i].nMin=min(segTree[i<<1].nMin,segTree[i<<1|1].nMin); segTree[i].nMax=max(segTree[i<<1].nMax,segTree[i<<1|1].nMax); } void Query(int i,int l,int r)//查询结点i上l-r的最大值和最小值 { if(segTree[i].nMax<=nMax&&segTree[i].nMin>=nMin) return; if(segTree[i].l==l&&segTree[i].r==r) { nMax=max(segTree[i].nMax,nMax); nMin=min(segTree[i].nMin,nMin); return; } int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) Query(i<<1,l,r); else if(l>mid) Query(i<<1|1,l,r); else { Query(i<<1,l,mid); Query(i<<1|1,mid+1,r); } } int main() { int n,q; int l,r; int i; while(scanf("%d%d",&n,&q)!=EOF) { for(i=1;i<=n;i++) scanf("%d",&a[i]); Build(1,1,n); for(i=1;i<=q;i++) { scanf("%d%d",&l,&r); nMax=-INF;nMin=INF; Query(1,l,r); printf("%d\n",nMax-nMin); } } return 0; }
93 / 406 Problem H HDU 4027 Can you answer these queries?
题意:给你N个数,有M个操作,操作有两类,
(1)"0 l r",表示将区间[l,r]里的每个数都开根号。
(2)"1 l r",表示查询区间[l,r]里所有数的和。
/* HDU 4027 Can you answer these queries? */ #include<stdio.h> #include<iostream> #include<math.h> using namespace std; const int MAXN=5000100; struct Node { int l,r; long long sum; }segTree[MAXN*3]; void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; if(l==r) { scanf("%I64d",&segTree[i].sum); return; } int mid=((l+r)>>1); Build(i<<1,l,mid); Build((i<<1)|1,mid+1,r); segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum; } void action(int i,int l,int r) { if(segTree[i].l==l && segTree[i].r==r && segTree[i].sum==r-l+1) return; if(segTree[i].l==segTree[i].r) { segTree[i].sum=sqrt(segTree[i].sum*1.0); return; } int mid=((segTree[i].l+segTree[i].r)>>1); if(r<=mid) action(i<<1,l,r); else if(l>mid) action((i<<1)|1,l,r); else { action(i<<1,l,mid); action((i<<1)|1,mid+1,r); } segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum; } long long SUM(int i,int l,int r) { if(l==segTree[i].l && r==segTree[i].r) return segTree[i].sum; int mid=((segTree[i].l+segTree[i].r)/2); long long ans=0; if(r<=mid) ans=SUM(i<<1,l,r); else if(l>mid) ans=SUM((i<<1)|1,l,r); else { ans+=SUM(i<<1,l,mid); ans+=SUM((i<<1)|1,mid+1,r); } return ans; } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n; int T,X,Y; int iCase=0; int M; while(scanf("%d",&n)!=EOF) { iCase++; Build(1,1,n); scanf("%d",&M); printf("Case #%d:\n",iCase); while(M--) { scanf("%d%d%d",&T,&X,&Y); if(X>Y)swap(X,Y);//这个很重要 if(T==0) action(1,X,Y); //else printf("%I64d\n",sum(1,X,Y)); else printf("%I64d\n",SUM(1,X,Y)); } printf("\n"); } return 0; }
73 / 183 Problem I HDU 1540 Tunnel Warfare
/* HDU 1540 Tunnel Warfare 题义是对于一段线段,D x 表示破坏x点, R 表示回复最近一次被破坏的点,Q x表示 询问以x点为中心的两头的最长的连续区间。 */ #include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; const int MAXN=50050; struct Node { int l,r; int ll,rl,ml; //左端点开始向左连续的最大长度和右端点开始向左最大的连续长度 //以及这个区间最大连续长度 }segTree[MAXN*3]; void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].ll=segTree[i].rl=segTree[i].ml=r-l+1; if(l==r)return; int mid=(l+r)>>1; Build(i<<1,l,mid); Build((i<<1)|1,mid+1,r); } void update(int i,int t,int val) { if(segTree[i].l==segTree[i].r) { if(val==1)segTree[i].ll=segTree[i].rl=segTree[i].ml=1; else segTree[i].ll=segTree[i].rl=segTree[i].ml=0; return; } int mid=(segTree[i].l+segTree[i].r)>>1; if(t<=mid)update(i<<1,t,val); else update((i<<1)|1,t,val); segTree[i].ll=segTree[i<<1].ll; segTree[i].rl=segTree[(i<<1)|1].rl; segTree[i].ml=max(segTree[i<<1].ml,segTree[(i<<1)|1].ml); segTree[i].ml=max(segTree[i].ml,segTree[i<<1].rl+segTree[(i<<1)|1].ll);//解还有可能在中间那部分 if(segTree[i<<1].ll==segTree[i<<1].r-segTree[i<<1].l+1)segTree[i].ll+=segTree[(i<<1)|1].ll; if(segTree[(i<<1)|1].rl==segTree[(i<<1)|1].r-segTree[(i<<1)|1].l+1) segTree[i].rl+=segTree[i<<1].rl; } int query(int i,int t) { if(segTree[i].l==segTree[i].r||segTree[i].ml==0||segTree[i].ml==segTree[i].r-segTree[i].l+1) { return segTree[i].ml; } int mid=(segTree[i].l+segTree[i].r)>>1; if(t<=mid) { if(t>=segTree[i<<1].r-segTree[i<<1].rl+1)//仔细想想 return query(i<<1,t)+query((i<<1)|1,mid+1); else return query(i<<1,t); } else { if(t<=segTree[(i<<1)|1].l+segTree[(i<<1)|1].ll-1) return query((i<<1)|1,t)+query(i<<1,mid); else return query((i<<1)|1,t); } } int que[MAXN]; int top; int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int n,m; char str[10]; int x; while(scanf("%d%d",&n,&m)!=EOF) { Build(1,1,n); top=0; while(m--) { scanf("%s",&str); if(str[0]=='D') { scanf("%d",&x); que[top++]=x; update(1,x,0); } else if(str[0]=='Q') { scanf("%d",&x); printf("%d\n",query(1,x)); } else { if(x>0) { x=que[--top]; update(1,x,1); } } } } return 0; }
61 / 164 Problem J HDU 3974 Assign the task
d.N个员工,N-1条有向边,构成一颗树,边v->u表示v是u的直接上属。有两种操作,
C x表示查询x员工当前的任务
T x y表示给x员工安排y任务
新的任务会覆盖前面的任务。
怎么转换成线段树的?从根节点开始进行一次dfs,上下属关系转换成区间包含关系。比较巧妙。
然后就是区间更新,单点查询。
/* *********************************************** Author :kuangbin Created Time :2013-11-17 19:50:24 File Name :E:\2013ACM\比赛练习\2013-11-17\C.cpp ************************************************ */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int MAXN = 50010; struct Edge { int to,next; }edge[MAXN]; int head[MAXN],tot; int cnt; int start[MAXN],_end[MAXN]; void init() { cnt = 0; tot = 0; memset(head,-1,sizeof(head)); } void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } void dfs(int u) { ++cnt; start[u] = cnt; for(int i = head[u];i != -1;i = edge[i].next) { dfs(edge[i].to); } _end[u] = cnt; } struct Node { int l,r; int val; int lazy; }segTree[MAXN*4]; void Update_Same(int r,int v) { if(r) { segTree[r].val = v; segTree[r].lazy = 1; } } void push_down(int r) { if(segTree[r].lazy) { Update_Same(r<<1,segTree[r].val); Update_Same((r<<1)|1,segTree[r].val); segTree[r].lazy = 0; } } void Build(int i,int l,int r) { segTree[i].l = l; segTree[i].r = r; segTree[i].val = -1; segTree[i].lazy = 0; if(l == r)return; int mid = (l+r)/2; Build(i<<1,l,mid); Build((i<<1)|1,mid+1,r); } void update(int i,int l,int r,int v) { if(segTree[i].l == l && segTree[i].r == r) { Update_Same(i,v); return; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)update(i<<1,l,r,v); else if(l > mid)update((i<<1)|1,l,r,v); else { update(i<<1,l,mid,v); update((i<<1)|1,mid+1,r,v); } } int query(int i,int u) { if(segTree[i].l == u && segTree[i].r == u) return segTree[i].val; push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(u <= mid)return query(i<<1,u); else return query((i<<1)|1,u); } bool used[MAXN]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; int T; scanf("%d",&T); int iCase = 0; while(T--) { iCase++; printf("Case #%d:\n",iCase); int u,v; memset(used,false,sizeof(used)); init(); scanf("%d",&n); for(int i = 1;i < n;i++) { scanf("%d%d",&u,&v); used[u] = true; addedge(v,u); } for(int i = 1;i <= n;i++) if(!used[i]) { dfs(i); break; } Build(1,1,cnt); char op[10]; int m; scanf("%d",&m); while(m--) { scanf("%s",op); if(op[0] == 'C') { scanf("%d",&u); printf("%d\n",query(1,start[u])); } else { scanf("%d%d",&u,&v); update(1,start[u],_end[u],v); } } } return 0; }
27 / 105 Problem K HDU 4578 Transformation
很裸的线段树的题目。但是做起来比较麻烦。
我用sum1,sum2,sum3分别代表和、平方和、立方和。
懒惰标记使用三个变量:
lazy1:是加的数
lazy2:是乘的倍数
lazy3:是赋值为一个常数,为0表示没有。
更新操作需要注意很多细节。
/* ********************************************** Author : kuangbin Created Time: 2013/8/10 13:24:03 File Name : F:\2013ACM练习\比赛练习\2013杭州邀请赛重现\1003.cpp *********************************************** */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> using namespace std; const int MOD = 10007; const int MAXN = 100010; struct Node { int l,r; int sum1,sum2,sum3; int lazy1,lazy2,lazy3; }segTree[MAXN*3]; void build(int i,int l,int r) { segTree[i].l = l; segTree[i].r = r; segTree[i].sum1 = segTree[i].sum2 = segTree[i].sum3 = 0; segTree[i].lazy1 = segTree[i].lazy3 = 0; segTree[i].lazy2 = 1; int mid = (l+r)/2; if(l == r)return; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } void push_up(int i) { if(segTree[i].l == segTree[i].r)//这个判断以前都没用过啊。。。不用会出错吗? return; segTree[i].sum1 = (segTree[i<<1].sum1 + segTree[(i<<1)|1].sum1)%MOD; segTree[i].sum2 = (segTree[i<<1].sum2 + segTree[(i<<1)|1].sum2)%MOD; segTree[i].sum3 = (segTree[i<<1].sum3 + segTree[(i<<1)|1].sum3)%MOD; } void push_down(int i) { if(segTree[i].l == segTree[i].r) return;// if(segTree[i].lazy3 != 0)//为什么先看lazy3呢?想了下,这个置数优先级更高。 { segTree[i<<1].lazy3 = segTree[(i<<1)|1].lazy3 = segTree[i].lazy3; segTree[i<<1].lazy1 = segTree[(i<<1)|1].lazy1 = 0; segTree[i<<1].lazy2 = segTree[(i<<1)|1].lazy2 = 1; segTree[i<<1].sum1 = (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i<<1].lazy3%MOD; segTree[i<<1].sum2 = (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i<<1].lazy3%MOD*segTree[i<<1].lazy3%MOD; segTree[i<<1].sum3 = (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i<<1].lazy3%MOD*segTree[i<<1].lazy3%MOD*segTree[i<<1].lazy3%MOD; segTree[(i<<1)|1].sum1 = (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[(i<<1)|1].lazy3%MOD; segTree[(i<<1)|1].sum2 = (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[(i<<1)|1].lazy3%MOD*segTree[(i<<1)|1].lazy3%MOD; segTree[(i<<1)|1].sum3 = (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[(i<<1)|1].lazy3%MOD*segTree[(i<<1)|1].lazy3%MOD*segTree[(i<<1)|1].lazy3%MOD; segTree[i].lazy3 = 0; } if(segTree[i].lazy1 != 0 || segTree[i].lazy2 != 1) { segTree[i<<1].lazy1 = ( segTree[i].lazy2*segTree[i<<1].lazy1%MOD + segTree[i].lazy1 )%MOD; segTree[i<<1].lazy2 = segTree[i<<1].lazy2*segTree[i].lazy2%MOD; int sum1,sum2,sum3; sum1 = (segTree[i<<1].sum1*segTree[i].lazy2%MOD + (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i].lazy1%MOD)%MOD; sum2 = (segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i<<1].sum2 % MOD + 2*segTree[i].lazy1*segTree[i].lazy2%MOD * segTree[i<<1].sum1%MOD + (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i].lazy1%MOD*segTree[i].lazy1%MOD)%MOD; sum3 = segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i<<1].sum3 % MOD; sum3 = (sum3 + 3*segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i<<1].sum2) % MOD; sum3 = (sum3 + 3*segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[i<<1].sum1) % MOD; sum3 = (sum3 + (segTree[i<<1].r - segTree[i<<1].l + 1)*segTree[i].lazy1%MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD; segTree[i<<1].sum1 = sum1; segTree[i<<1].sum2 = sum2; segTree[i<<1].sum3 = sum3; segTree[(i<<1)|1].lazy1 = ( segTree[i].lazy2*segTree[(i<<1)|1].lazy1%MOD + segTree[i].lazy1 )%MOD; segTree[(i<<1)|1].lazy2 = segTree[(i<<1)|1].lazy2 * segTree[i].lazy2 % MOD; sum1 = (segTree[(i<<1)|1].sum1*segTree[i].lazy2%MOD + (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[i].lazy1%MOD)%MOD; sum2 = (segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[(i<<1)|1].sum2 % MOD + 2*segTree[i].lazy1*segTree[i].lazy2%MOD * segTree[(i<<1)|1].sum1%MOD + (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[i].lazy1%MOD*segTree[i].lazy1%MOD)%MOD; sum3 = segTree[i].lazy2 * segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[(i<<1)|1].sum3 % MOD; sum3 = (sum3 + 3*segTree[i].lazy2 % MOD * segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[(i<<1)|1].sum2) % MOD; sum3 = (sum3 + 3*segTree[i].lazy2 % MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD * segTree[(i<<1)|1].sum1) % MOD; sum3 = (sum3 + (segTree[(i<<1)|1].r - segTree[(i<<1)|1].l + 1)*segTree[i].lazy1%MOD * segTree[i].lazy1 % MOD * segTree[i].lazy1 % MOD) % MOD; segTree[(i<<1)|1].sum1 = sum1; segTree[(i<<1)|1].sum2 = sum2; segTree[(i<<1)|1].sum3 = sum3; segTree[i].lazy1 = 0; segTree[i].lazy2 = 1; } } void update(int i,int l,int r,int type,int c) { if(segTree[i].l == l && segTree[i].r == r) { c %= MOD; if(type == 1) { segTree[i].lazy1 += c; segTree[i].lazy1 %= MOD; segTree[i].sum3 = (segTree[i].sum3 + 3*segTree[i].sum2%MOD*c%MOD + 3*segTree[i].sum1%MOD*c%MOD*c%MOD + (segTree[i].r - segTree[i].l + 1)*c%MOD*c%MOD*c%MOD)%MOD; segTree[i].sum2 = (segTree[i].sum2 + 2*segTree[i].sum1%MOD*c%MOD + (segTree[i].r - segTree[i].l + 1)*c%MOD*c%MOD)%MOD; segTree[i].sum1 = (segTree[i].sum1 + (segTree[i].r - segTree[i].l + 1)*c%MOD)%MOD; } else if(type == 2) { segTree[i].lazy1 = segTree[i].lazy1*c%MOD; segTree[i].lazy2 = segTree[i].lazy2*c%MOD; segTree[i].sum1 = segTree[i].sum1*c%MOD; segTree[i].sum2 = segTree[i].sum2*c%MOD*c%MOD; segTree[i].sum3 = segTree[i].sum3*c%MOD*c%MOD*c%MOD; } else { segTree[i].lazy1 = 0; segTree[i].lazy2 = 1; segTree[i].lazy3 = c%MOD; segTree[i].sum1 = c*(segTree[i].r - segTree[i].l + 1)%MOD; segTree[i].sum2 = c*(segTree[i].r - segTree[i].l + 1)%MOD*c%MOD; segTree[i].sum3 = c*(segTree[i].r - segTree[i].l + 1)%MOD*c%MOD*c%MOD; } return; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)update(i<<1,l,r,type,c); else if(l > mid)update((i<<1)|1,l,r,type,c); else { update(i<<1,l,mid,type,c); update((i<<1)|1,mid+1,r,type,c); } push_up(i); } int query(int i,int l,int r,int p) { if(segTree[i].l == l && segTree[i].r == r) { if(p == 1)return segTree[i].sum1; else if(p== 2)return segTree[i].sum2; else return segTree[i].sum3; } push_down(i); int mid = (segTree[i].l + segTree[i].r )/2; if(r <= mid)return query(i<<1,l,r,p); else if(l > mid)return query((i<<1)|1,l,r,p); else return (query(i<<1,l,mid,p)+query((i<<1)|1,mid+1,r,p))%MOD; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n,m; while(scanf("%d%d",&n,&m) == 2) { if(n == 0 && m == 0)break; build(1,1,n); int type,x,y,c; while(m--) { scanf("%d%d%d%d",&type,&x,&y,&c); if(type == 4)printf("%d\n",query(1,x,y,c)); else update(1,x,y,type,c); } } return 0; }
32 / 103 Problem L HDU 4614 Vases and Flowers
很裸的线段树。但是写起来很麻烦。
1~N 的区间,用1表示空的,0表示放了花的。
维护一个sum,就是和
first : 区间最左边的1
last: 区间最右边的1
然后一个更新操作,一个求区间和操作。
查询区间第一个1,和最后一个1.
二分确定区间。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <set> #include <map> #include <vector> #include <queue> #include <string> #include <math.h> using namespace std; const int MAXN = 50010; struct Node { int l,r; int sum; int lazy; int first; int last; }segTree[MAXN*3]; void push_up(int i) { if(segTree[i].l==segTree[i].r)return; segTree[i].sum = segTree[i<<1].sum+segTree[(i<<1)|1].sum; if(segTree[i<<1].first != -1)segTree[i].first = segTree[i<<1].first; else segTree[i].first = segTree[(i<<1)|1].first; if(segTree[(i<<1)|1].last != -1)segTree[i].last = segTree[(i<<1)|1].last; else segTree[i].last = segTree[(i<<1)].last; } void push_down(int i) { if(segTree[i].r == segTree[i].l)return; if(segTree[i].lazy==1) { segTree[i<<1].first = segTree[i<<1].l; segTree[i<<1].last = segTree[i<<1].r; segTree[i<<1].sum = segTree[i<<1].r-segTree[i<<1].l+1; segTree[i<<1].lazy=1; segTree[(i<<1)|1].first = segTree[(i<<1)|1].l; segTree[(i<<1)|1].last = segTree[(i<<1)|1].r; segTree[(i<<1)|1].sum = segTree[(i<<1)|1].r-segTree[(i<<1)|1].l+1; segTree[(i<<1)|1].lazy=1; } if(segTree[i].lazy == -1) { segTree[i<<1].first = -1; segTree[i<<1].last = -1; segTree[i<<1].sum = 0; segTree[i<<1].lazy=-1; segTree[(i<<1)|1].first = -1; segTree[(i<<1)|1].last = -1; segTree[(i<<1)|1].sum = 0; segTree[(i<<1)|1].lazy=-1; } segTree[i].lazy = 0; } void build(int i,int l,int r) { segTree[i].l = l; segTree[i].r = r; segTree[i].sum = r-l+1; segTree[i].lazy = 0; segTree[i].first = l; segTree[i].last = r; if(l==r)return ; int mid = (l+r)/2; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); } void update(int i,int l,int r,int type) { if(segTree[i].l == l && segTree[i].r==r) { if(type == 0) { if(segTree[i].sum == 0)return; segTree[i].sum = 0; segTree[i].lazy = -1; segTree[i].first = -1; segTree[i].last = -1; return; } else if(type == 1) { if(segTree[i].sum == segTree[i].r-segTree[i].l+1)return; segTree[i].sum = segTree[i].r-segTree[i].l+1; segTree[i].lazy = 1; segTree[i].first = segTree[i].l; segTree[i].last = segTree[i].r; return; } } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)update(i<<1,l,r,type); else if(l > mid)update((i<<1)|1,l,r,type); else { update(i<<1,l,mid,type); update((i<<1)|1,mid+1,r,type); } push_up(i); } int sum(int i,int l,int r) { if(segTree[i].l == l && segTree[i].r == r) { return segTree[i].sum; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)return sum(i<<1,l,r); else if(l > mid)return sum((i<<1)|1,l,r); else return sum((i<<1)|1,mid+1,r)+sum(i<<1,l,mid); } int n,m; int query1(int i,int l,int r) { if(segTree[i].l == l && segTree[i].r == r) { return segTree[i].first; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; int ans1,ans2; if(r <= mid)return query1(i<<1,l,r); else if(l > mid)return query1((i<<1)|1,l,r); else { ans1 = query1(i<<1,l,mid); if(ans1 != -1)return ans1; return query1((i<<1)|1,mid+1,r); } } int query2(int i,int l,int r) { if(segTree[i].l == l && segTree[i].r == r) { return segTree[i].last; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; int ans1,ans2; if(r <= mid)return query2(i<<1,l,r); else if(l > mid)return query2((i<<1)|1,l,r); else { ans1 = query2((i<<1)|1,mid+1,r); if(ans1 != -1)return ans1; return query2(i<<1,l,mid); } } int bisearch(int A,int F) { if(sum(1,A,n)==0)return -1; if(sum(1,A,n)<F)return n; int l= A,r = n; int ans=A; while(l<=r) { int mid = (l+r)/2; if(sum(1,A,mid)>=F) { ans = mid; r = mid-1; } else l = mid+1; } return ans; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); build(1,1,n); int op,u,v; while(m--) { scanf("%d%d%d",&op,&u,&v); if(op == 1) { u++; int t = bisearch(u,v); //printf("t:%d\n",t); if(t==-1) { printf("Can not put any one.\n"); continue; } printf("%d %d\n",query1(1,u,t)-1,query2(1,u,t)-1); update(1,u,t,0); } else { u++;v++; //printf("sum:%d\n",sum(1,u,v)); printf("%d\n",v-u+1-sum(1,u,v)); update(1,u,v,1); } } printf("\n"); } return 0; }
29 / 139 Problem M HDU 4553 约会安排
//============================================================================ // Name : C.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <map> #include <queue> #include <set> #include <vector> #include <string> #include <math.h> using namespace std; const int MAXN=100010; struct Node { int l,r; int Max,Lmax,Rmax; int Max1,Lmax1,Rmax1; }segTree[MAXN*4]; void push_up(int x) { if(segTree[x].l==segTree[x].r)return; int lson=2*x; int rson=2*x+1; segTree[x].Lmax=segTree[lson].Lmax; if(segTree[lson].Lmax==segTree[lson].r-segTree[lson].l+1)segTree[x].Lmax+=segTree[rson].Lmax; segTree[x].Rmax=segTree[rson].Rmax; if(segTree[rson].Rmax==segTree[rson].r-segTree[rson].l+1)segTree[x].Rmax+=segTree[lson].Rmax; segTree[x].Max=max(segTree[lson].Max,segTree[rson].Max); segTree[x].Max=max(segTree[x].Max,max(segTree[x].Lmax,segTree[x].Rmax)); segTree[x].Max=max(segTree[x].Max,segTree[lson].Rmax+segTree[rson].Lmax); segTree[x].Lmax1=segTree[lson].Lmax1; if(segTree[lson].Lmax1==segTree[lson].r-segTree[lson].l+1)segTree[x].Lmax1+=segTree[rson].Lmax1; segTree[x].Rmax1=segTree[rson].Rmax1; if(segTree[rson].Rmax1==segTree[rson].r-segTree[rson].l+1)segTree[x].Rmax1+=segTree[lson].Rmax1; segTree[x].Max1=max(segTree[lson].Max1,segTree[rson].Max1); segTree[x].Max1=max(segTree[x].Max1,max(segTree[x].Lmax1,segTree[x].Rmax1)); segTree[x].Max1=max(segTree[x].Max1,segTree[lson].Rmax1+segTree[rson].Lmax1); } void push_down(int x) { if(segTree[x].l==segTree[x].r)return; int lson=2*x,rson=2*x+1; if(segTree[x].Max==0) { segTree[lson].Max=segTree[lson].Lmax=segTree[lson].Rmax=0; segTree[rson].Max=segTree[rson].Lmax=segTree[rson].Rmax=0; } if(segTree[x].Max==segTree[x].r-segTree[x].l+1) { segTree[lson].Max=segTree[lson].Lmax=segTree[lson].Rmax=segTree[lson].r-segTree[lson].l+1; segTree[rson].Max=segTree[rson].Lmax=segTree[rson].Rmax=segTree[rson].r-segTree[rson].l+1; } if(segTree[x].Max1==0) { segTree[lson].Max1=segTree[lson].Lmax1=segTree[lson].Rmax1=0; segTree[rson].Max1=segTree[rson].Lmax1=segTree[rson].Rmax1=0; } if(segTree[x].Max1==segTree[x].r-segTree[x].l+1) { segTree[lson].Max1=segTree[lson].Lmax1=segTree[lson].Rmax1=segTree[lson].r-segTree[lson].l+1; segTree[rson].Max1=segTree[rson].Lmax1=segTree[rson].Rmax1=segTree[rson].r-segTree[rson].l+1; } } void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].Max=segTree[i].Lmax=segTree[i].Rmax=r-l+1; segTree[i].Max1=segTree[i].Lmax1=segTree[i].Rmax1=r-l+1; if(l==r)return; int mid=(l+r)/2; Build(i<<1,l,mid); Build((i<<1)|1,mid+1,r); } int query(int i,int x) { if(segTree[i].Max<x)return 0; if(segTree[i].Lmax>=x)return segTree[i].l; if(segTree[i<<1].Max>=x)return query(i<<1,x); if(segTree[i<<1].Rmax+segTree[(i<<1)|1].Lmax>=x)return segTree[i<<1].r-segTree[i<<1].Rmax+1; return query((i<<1)|1,x); } int query1(int i,int x) { if(segTree[i].Max1<x)return 0; if(segTree[i].Lmax1>=x)return segTree[i].l; if(segTree[i<<1].Max1>=x)return query1(i<<1,x); if(segTree[i<<1].Rmax1+segTree[(i<<1)|1].Lmax1>=x)return segTree[i<<1].r-segTree[i<<1].Rmax1+1; return query1((i<<1)|1,x); } void update(int i,int l,int r) { if(segTree[i].l==l && segTree[i].r==r) { segTree[i].Max=segTree[i].Lmax=segTree[i].Rmax=segTree[i].r-segTree[i].l+1; segTree[i].Max1=segTree[i].Lmax1=segTree[i].Rmax1=segTree[i].r-segTree[i].l+1; return; } push_down(i); int mid=(segTree[i].l+segTree[i].r)/2; if(r<=mid)update(i<<1,l,r); else if(l>mid) update((i<<1)|1,l,r); else { update(i<<1,l,mid); update((i<<1)|1,mid+1,r); } push_up(i); } void zhan1(int i,int l,int r) { if(segTree[i].l==l && segTree[i].r==r) { segTree[i].Max=segTree[i].Lmax=segTree[i].Rmax=0; return; } push_down(i); int mid=(segTree[i].l+segTree[i].r)/2; if(r<=mid)zhan1(i<<1,l,r); else if(l>mid) zhan1((i<<1)|1,l,r); else { zhan1(i<<1,l,mid); zhan1((i<<1)|1,mid+1,r); } push_up(i); } void zhan2(int i,int l,int r) { if(segTree[i].l==l && segTree[i].r==r) { segTree[i].Max=segTree[i].Lmax=segTree[i].Rmax=0; segTree[i].Max1=segTree[i].Lmax1=segTree[i].Rmax1=0; return; } push_down(i); int mid=(segTree[i].l+segTree[i].r)/2; if(r<=mid)zhan2(i<<1,l,r); else if(l>mid) zhan2((i<<1)|1,l,r); else { zhan2(i<<1,l,mid); zhan2((i<<1)|1,mid+1,r); } push_up(i); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); int T; int n,m; int l,r; int x; char op[20]; scanf("%d",&T); int iCase=0; while(T--) { iCase++; printf("Case %d:\n",iCase); scanf("%d%d",&n,&m); Build(1,1,n); while(m--) { scanf("%s",op); if(op[0]=='D') { scanf("%d",&x); int tmp=query(1,x); if(tmp==0)printf("fly with yourself\n"); else { zhan1(1,tmp,tmp+x-1); printf("%d,let's fly\n",tmp); } } else if(op[0]=='N') { scanf("%d",&x); int tmp=query(1,x); if(tmp!=0) { zhan2(1,tmp,tmp+x-1); printf("%d,don't put my gezi\n",tmp); continue; } tmp=query1(1,x); if(tmp!=0) { zhan2(1,tmp,tmp+x-1); printf("%d,don't put my gezi\n",tmp); continue; } printf("wait for me\n"); } else { scanf("%d%d",&l,&r); printf("I am the hope of chinese chengxuyuan!!\n"); update(1,l,r); } } } return 0; }
35 / 52 Problem N POJ 1177 Picture
总体思路:
1.沿X轴离散化建树
2.按Y值从小到大排序平行与X轴的边,然后顺序处理
如果遇到矩形下面那条边则插入到线段树中,遇到矩形上面的边则将相应的边删除掉
根据线段树当前的状态统计长度
第二点是本题的核心思想,偶再举个例:
第一次求出的部分很好理解.
第二次求出的为什么会少了中间那部分.那是因为插入的新线段覆盖了第一条,此时线段树返回的长度是新的那一条的长度,将这个值再减去上次的就少了中间那部分
第三次因为是矩形的上边,所以要删除在那条长的线段.此时的线段树返回的则是第一次的长度,将此值减去第二次返回值,再取其负值就是红色X轴那部分了
最后那条X轴的,再补上就行了.
需要注意的就是离散化以后一定要去掉重复元素,否则会出错的。
ps:不懂这种离散化的时候为什么去除重复元素?好像懂了一点点。。不过还是先记住吧,日后有机会再细细研究。
/* POJ 1177 矩形周长并,求轮廓周长 这题可以不离散化做的,我做的是离散化以后的 这题和矩形面积并有类似的地方 把矩形变成一条条平行于x轴的线段(当然弄成平行于y轴的也是可以的) 要累加的一个是覆盖的线段长度的变化,还有就是竖直的线段。 离散化以后一定要进行去掉重复的点,因为会影响后面合并的时候。 STL中的unique()函数可以快速去掉相同元素 */ #include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> using namespace std; const int MAXN=10010; struct Node { int l,r; int cnt;//有效长度 int lf,rf;//实际的左右端点 int numseg;//分支数,一个分支对应两条竖线 int c;//记录覆盖情况 bool lcover,rcover; }segTree[MAXN*4]; struct Line { int y; int x1,x2; int f; }line[MAXN]; bool cmp(Line a,Line b) { return a.y<b.y; } int x[MAXN]; void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].lf=x[l]; segTree[i].rf=x[r]; segTree[i].cnt=0; segTree[i].numseg=0; segTree[i].c=0; segTree[i].lcover=segTree[i].rcover=false; if(l+1==r)return; int mid=(l+r)/2; Build(i<<1,l,mid); Build((i<<1)|1,mid,r); } void calen(int i) { if(segTree[i].c>0) { segTree[i].cnt=segTree[i].rf-segTree[i].lf; segTree[i].numseg=1; segTree[i].lcover=segTree[i].rcover=true; return; } if(segTree[i].l+1==segTree[i].r) { segTree[i].cnt=0; segTree[i].numseg=0; segTree[i].lcover=segTree[i].rcover=false; } else { segTree[i].cnt=segTree[i<<1].cnt+segTree[(i<<1)|1].cnt; segTree[i].lcover=segTree[i<<1].lcover; segTree[i].rcover=segTree[(i<<1)|1].rcover; segTree[i].numseg=segTree[i<<1].numseg+segTree[(i<<1)|1].numseg; if(segTree[i<<1].rcover&&segTree[(i<<1)|1].lcover)segTree[i].numseg--; } } void update(int i,Line e) { if(segTree[i].lf==e.x1&&segTree[i].rf==e.x2) { segTree[i].c+=e.f; calen(i); return; } if(e.x2<=segTree[i<<1].rf)update(i<<1,e); else if(e.x1>=segTree[(i<<1)|1].lf)update((i<<1)|1,e); else { Line temp=e; temp.x2=segTree[i<<1].rf; update(i<<1,temp); temp=e; temp.x1=segTree[(i<<1)|1].lf; update((i<<1)|1,temp); } calen(i); } int main() { int x1,y1,x2,y2; int n; while(scanf("%d",&n)==1) { int t=0; for(int i=0;i<n;i++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); line[t].x1=x1; line[t].x2=x2; line[t].y=y1; line[t].f=1; x[t++]=x1; line[t].x1=x1; line[t].x2=x2; line[t].y=y2; line[t].f=-1; x[t++]=x2; } sort(line,line+t,cmp); sort(x,x+t); int m=unique(x,x+t)-x;//合并相同元素,这里一点要合并相同元素,否则会WA. Build(1,0,m-1); int ans=0; int last=0; for(int i=0;i<t-1;i++) { update(1,line[i]); ans+=segTree[1].numseg*2*(line[i+1].y-line[i].y); ans+=abs(segTree[1].cnt-last); last=segTree[1].cnt; } update(1,line[t-1]); ans+=abs(segTree[1].cnt-last); printf("%d\n",ans); } return 0; }
37 / 67 Problem O HDU 1255 覆盖的面积
d.求面积交
hint.解题报告见之前博客
40 / 88 Problem P HDU 1542 Atlantis
d.求面积并,比求面积交简单
/* POJ 1151 Atlantis 求矩形并的面积(线段树+离散化) */ #include<stdio.h> #include<iostream> #include<algorithm> using namespace std; #define MAXN 201 struct Node { int l,r;//线段树的左右整点 int c;//c用来记录重叠情况 double cnt,lf,rf;// //cnt用来计算实在的长度,rf,lf分别是对应的左右真实的浮点数端点 }segTree[MAXN*3]; struct Line { double x,y1,y2; int f; }line[MAXN]; //把一段段平行于y轴的线段表示成数组 , //x是线段的x坐标,y1,y2线段对应的下端点和上端点的坐标 //一个矩形 ,左边的那条边f为1,右边的为-1, //用来记录重叠情况,可以根据这个来计算,nod节点中的c bool cmp(Line a,Line b)//sort排序的函数 { return a.x < b.x; } double y[MAXN];//记录y坐标的数组 void Build(int t,int l,int r)//构造线段树 { segTree[t].l=l;segTree[t].r=r; segTree[t].cnt=segTree[t].c=0; segTree[t].lf=y[l]; segTree[t].rf=y[r]; if(l+1==r) return; int mid=(l+r)>>1; Build(t<<1,l,mid); Build(t<<1|1,mid,r);//递归构造 } void calen(int t)//计算长度 { if(segTree[t].c>0) { segTree[t].cnt=segTree[t].rf-segTree[t].lf; return; } if(segTree[t].l+1==segTree[t].r) segTree[t].cnt=0; else segTree[t].cnt=segTree[t<<1].cnt+segTree[t<<1|1].cnt; } void update(int t,Line e)//加入线段e,后更新线段树 { if(e.y1==segTree[t].lf&&e.y2==segTree[t].rf) { segTree[t].c+=e.f; calen(t); return; } if(e.y2<=segTree[t<<1].rf) update(t<<1,e); else if(e.y1>=segTree[t<<1|1].lf) update(t<<1|1,e); else { Line tmp=e; tmp.y2=segTree[t<<1].rf; update(t<<1,tmp); tmp=e; tmp.y1=segTree[t<<1|1].lf; update(t<<1|1,tmp); } calen(t); } int main() { int i,n,t,iCase=0; double x1,y1,x2,y2; while(scanf("%d",&n),n) { iCase++; t=1; for(i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[t].x=x1; line[t].y1=y1; line[t].y2=y2; line[t].f=1; y[t]=y1; t++; line[t].x=x2; line[t].y1=y1; line[t].y2=y2; line[t].f=-1; y[t]=y2; t++; } sort(line+1,line+t,cmp); sort(y+1,y+t); Build(1,1,t-1); update(1,line[1]); double res=0; for(i=2;i<t;i++) { res+=segTree[1].cnt*(line[i].x-line[i-1].x); update(1,line[i]); } printf("Test case #%d\nTotal explored area: %.2f\n\n",iCase,res); //看来POJ上%.2f可以过,%.2lf却不行了 } return 0; }
7 / 15 Problem Q HDU 3642 Get The Treasury
求立方体相交至少3次的体积。
枚举z
然后求矩形面积交。
常规的线段树题目。
较麻烦
/* *HDU 3642 给定一些长方体,求这些长方体相交至少3次的体积 z坐标的绝对值不超过500,所以枚举z坐标,然后求矩形面积并 */ #include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> using namespace std; const int MAXN=2010; struct Node { int l,r; int lf,rf;//实际的左右端点 int c; int once,twice,more; }segTree[MAXN*3]; int y[MAXN]; int z[MAXN]; struct Line { int x; int y1,y2; int z1,z2;//这两个是枚举的时候判断使用的 int f; }line[MAXN]; bool cmp(Line a,Line b) { return a.x<b.x; } void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].lf=y[l]; segTree[i].rf=y[r]; segTree[i].c=0; segTree[i].once=segTree[i].twice=segTree[i].more=0; if(r==l+1)return; int mid=(l+r)>>1; Build(i<<1,l,mid); Build((i<<1)|1,mid,r); } void push_up(int i) { if(segTree[i].c>2) { segTree[i].more=segTree[i].rf-segTree[i].lf; segTree[i].once=segTree[i].twice=0; } else if(segTree[i].c==2) { if(segTree[i].l+1==segTree[i].r)//叶子结点 { segTree[i].more=0; segTree[i].twice=segTree[i].rf-segTree[i].lf; segTree[i].once=0; return; } segTree[i].more=segTree[i<<1].once+segTree[i<<1].twice+segTree[i<<1].more +segTree[(i<<1)|1].once+segTree[(i<<1)|1].twice+segTree[(i<<1)|1].more; segTree[i].twice=segTree[i].rf-segTree[i].lf-segTree[i].more; segTree[i].once=0; } else if(segTree[i].c==1) { if(segTree[i].l+1==segTree[i].r) { segTree[i].more=0; segTree[i].twice=0; segTree[i].once=segTree[i].rf-segTree[i].lf; return; } segTree[i].more=segTree[i<<1].more+segTree[i<<1].twice +segTree[(i<<1)|1].more+segTree[(i<<1)|1].twice; segTree[i].twice=segTree[i<<1].once+segTree[(i<<1)|1].once; segTree[i].once=segTree[i].rf-segTree[i].lf-segTree[i].more-segTree[i].twice; } else { if(segTree[i].l+1==segTree[i].r) { segTree[i].more=segTree[i].once=segTree[i].twice=0; return; } segTree[i].more=segTree[i<<1].more+segTree[(i<<1)|1].more; segTree[i].twice=segTree[i<<1].twice+segTree[(i<<1)|1].twice; segTree[i].once=segTree[i<<1].once+segTree[(i<<1)|1].once; } } void update(int i,Line e) { if(segTree[i].lf>=e.y1 && segTree[i].rf<=e.y2) { segTree[i].c+=e.f; push_up(i); return; } if(e.y2<=segTree[i<<1].rf) update(i<<1,e); else if(e.y1>=segTree[(i<<1)|1].lf) update((i<<1)|1,e); else { Line temp=e; temp.y2=segTree[i<<1].rf; update(i<<1,temp); temp=e; temp.y1=segTree[(i<<1)|1].lf; update((i<<1)|1,temp); } push_up(i); } Line temp[MAXN]; int main() { int T; int n; int x1,y1,z1,x2,y2,z2; scanf("%d",&T); int iCase=0; while(T--) { iCase++; scanf("%d",&n); int t=0; for(int i=0;i<n;i++) { scanf("%d%d%d%d%d%d",&x1,&y1,&z1,&x2,&y2,&z2); line[t].x=x1; line[t].y1=y1; line[t].y2=y2; line[t].z1=z1; line[t].z2=z2; line[t].f=1; y[t]=y1; z[t++]=z1; line[t].x=x2; line[t].y1=y1; line[t].y2=y2; line[t].z1=z1; line[t].z2=z2; line[t].f=-1; y[t]=y2; z[t++]=z2; } sort(line,line+t,cmp); sort(y,y+t); int t1=unique(y,y+t)-y; Build(1,0,t1-1); sort(z,z+t); int t2=unique(z,z+t)-z; long long ans=0; long long area=0; for(int i=0;i<t2-1;i++) { int m=0; for(int j=0;j<t;j++) if(line[j].z1<=z[i]&&line[j].z2>z[i]) temp[m++]=line[j]; area=0; update(1,temp[0]); for(int j=1;j<m;j++) { area+=(long long)segTree[1].more*(temp[j].x-temp[j-1].x); update(1,temp[j]); } ans+=area*(z[i+1]-z[i]); } printf("Case %d: %I64d\n",iCase,ans); } return 0; }