【POJ 2528 Mayor's posters】
每个海报会覆盖一段连续的区间,所以这是个区间覆盖问题,可以用线段树。
但硬上nlogm虽然不会tle,但会mle,所以要离散化。
在[1,10000000]的这个瓷砖里,只有10000级别的修改,我们可以想象出整个区间可以被划分为多个被cover情况【一致】的小区间。那什么样的小区间被cover情况一致呢,所有端点排序后相邻端点构成的区间被cover情况是一致的,为什么呢?因为每次的覆盖都必定是两两端点间的。
这样的话每次cover就是cover这个海报start所代表的区间到end所代表的区间。
【注意要从后往前贴海报】
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define MAXN 20010 #define maxnode 100000 #define ll long long #define lowbit(x) (x&(-x)) int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int l[MAXN],r[MAXN],a[MAXN]; map<int,int> mp; struct node{ int l,r; bool covered; int d;//有没有被cover,有多少部分被cover不重要,只关心这个区间有没有被【完全】cover node *ls,*rs; }pool[MAXN*4]; int top; node* buildT(int l,int r){ node* p = pool + (++top); p->l=l; p->r=r; p->covered=false; p->d=0; if( l==r ) return p; p->ls = buildT(l,(l+r)/2); p->rs = buildT( (l+r)/2+1,r ); return p; } void update(node* p,int l,int r){//覆盖上这个区间 if( p->l==l && p->r==r ){ p->d=1; return; } int mid=(p->l+p->r)/2; if( mid>=r ) update(p->ls,l,r); else if( l>mid ) update(p->rs,l,r); else{ update(p->ls,l,mid); update(p->rs,mid+1,r); } p->d = p->d | ( p->ls->d&p->rs->d); } bool query(node* p,int l,int r){//询问这个区间有没有被完全覆盖 if( p->l==l && p->r==r ) return p->d; if( p->d ) return true;//p->d为1的话,省去了pushdown == 况且p->d为0的话也不能pushdown int mid=(p->l+p->r)/2; if( mid>=r ) return query(p->ls,l,r); else if( l>mid ) return query(p->rs,l,r); return query(p->ls,l,mid)&query(p->rs,mid+1,r); } int main(){ //ios::sync_with_stdio(false); int t; scanf("%d",&t); while(t--){ int n; scanf("%d",&n); int cnt=0,id=0; for(int i=1;i<=n;i++){ scanf("%d%d",&l[i],&r[i]); a[++id]=l[i]; a[++id]=r[i]; } sort(a+1,a+1+id); int num=unique(a+1,a+1+id)-(a+1); for(int i=1;i<=num;i++) mp[ a[i] ] = i; top=0; node* root = buildT(1,num); for(int i=n;i>=1;i--){ if( !query(root,mp[ l[i] ],mp[ r[i] ]) ) cnt++; update(root,mp[ l[i] ],mp[ r[i] ]); } printf("%d\n",cnt); } return 0; }
【HDU 2698 Just a Hook】
对于线段树我想的是每个node对应一个区间 [l,r] ,并且会用d存一个这个区间里所记录的值,那这涉及到一个问题是对于终止结点该怎么修改,修改后要怎么反馈到上层结点(即push up)。
然后更新d值我会从两个方面考虑,一就是根据题意随便搞,二是考虑下贡献。对于这题的话就是随便搞,然后记得加一个tag(延迟更新)记录这个区间是什么颜色,以后询问或更新以下的区间的话要pushdown下去。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define MAXN 200010 #define maxnode 100000 #define ll long long #define lowbit(x) (x&(-x)) int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; struct node{ int l,r; int d,tag; node *ls,*rs; }pool[4*MAXN]; int top; node* buildT(int l,int r){ node* p = pool + (++top); p->l=l; p->r=r; p->tag=0;//以下的都是tag类型的hook if( l==r ){ p->d=1; return p; } int mid = (l+r)/2; p->ls = buildT(l,mid); p->rs = buildT(mid+1,r); p->d = p->ls->d+p->rs->d; return p; } int getData(node* p){ if(p->tag==0) return p->d; return (p->r-p->l+1)*p->tag; } void pushdown(node* p){ if(p->tag==0) return; p->ls->tag = p->tag; p->rs->tag = p->tag; p->d = getData(p); p->tag=0; } void update(node* p,int l,int r,int type){ if( p->l==l && p->r==r ){ p->tag=type; return; } pushdown(p);//顺序重要 int mid=(p->r+p->l)/2; if( r<=mid ) update(p->ls,l,r,type); else if( l>mid ) update(p->rs,l,r,type); else{ update(p->ls,l,mid,type); update(p->rs,mid+1,r,type); } p->d = getData(p->ls)+getData(p->rs); } int query(node* p,int l,int r){ if( p->l==l && p->r==r ) return getData(p); pushdown(p); int mid = (p->l+p->r)/2; if( r<=mid ) return query(p->ls,l,r); else if( l>mid ) return query(p->rs,l,r); return query(p->ls,l,mid)+query(p->rs,mid+1,r); } int main(){ //ios::sync_with_stdio(false); int t; scanf("%d",&t); for(int i=1;i<=t;i++){ int n,q; scanf("%d%d",&n,&q); top=0; node* root = buildT(1,n); for(int j=1;j<=q;j++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); update(root,x,y,z); } printf("Case %d: The total value of the hook is %d.\n",i,query(root,1,n)); } return 0; }
【ZOJ 1610 Count the Colors】
这题算是我很喜欢的一道线段树题了, 没有被虐就没有热爱。感觉这题好是好在在线段树里加入了【暴力】,就很棒
原本想的是logn回答的话,那就是要用logn级别个终止结点拼凑出答案,那每个结点岂不是都要存一下有哪些颜色段,而且颜色段分别是什么?但这样太复杂了
需要敏锐地捕捉到对于每个data set只会有一次询问,那么O(n)的复杂度处理询问是可以接受的喂!
想到这点还不够,还要能想到那询问的时候把所有颜色都推到【叶子节点】看叶子结点是什么颜色就好了啊,就这么做完了。
而且注意到这里有个好处是由于dfs那么叶子节点的访问顺序一定是[1,1] , [2,2] , [3,3] , ... , [n,n],借此可以算出整个颜色段
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define MAXN 200010 #define maxnode 100000 #define ll long long #define lowbit(x) (x&(-x)) int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; struct node{ int l,r; int tag; node *ls,*rs; }pool[4*MAXN]; int ans[MAXN]; int top; node* buildT(int l,int r){ node* p = pool + (++top); p->l=l; p->r=r; p->tag=-1; if( l==r ) return p; int mid = (l+r)>>1; p->ls = buildT( l,mid ); p->rs = buildT( mid+1,r ); return p; } void pushdown(node* p){ if( p->tag==-1 ) return; p->ls->tag=p->tag; p->rs->tag=p->tag; p->tag=-1; } void update(node* p,int l,int r,int dx){ if( p->l==l && p->r==r ){ p->tag = dx; return; } int mid = (p->l+p->r)>>1; pushdown(p); if( r<=mid ) update(p->ls,l,r,dx); else if( l>mid ) update(p->rs,l,r,dx); else{ update(p->ls,l,mid,dx); update(p->rs,mid+1,r,dx); } } int last; void query(node* p,int l,int r){ if( l==r ){ if( p->tag!=-1 && p->tag!=last ) ans[ p->tag ]++; last=p->tag; return; } pushdown(p); int mid = (p->l+p->r)>>1; if( r<mid ) query(p->ls,l,r); else if( l>mid ) query(p->rs,l,r); else{ query(p->ls,l,mid); query(p->rs,mid+1,r); } } int main(){ //ios::sync_with_stdio(false); int n; while( scanf("%d",&n)!=EOF ){ top=0; node* root = buildT(1,8000); for(int i=1;i<=n;i++){ int x1,x2,c; scanf("%d%d%d",&x1,&x2,&c); update(root,x1+1,x2,c); } memset(ans,0,sizeof(ans)); last=-1; query(root,1,8000); for(int i=0;i<=8000;i++){ if( ans[i] ) printf("%d %d\n",i,ans[i]); } printf("\n"); } return 0; }
【HDU 4027 Can you answer these queries?】
这题是要想到一个比较妙的性质然后就能做了,不过这题坑点挺多的,真是难度不够坑来凑。。
主要还是不知道要怎么更新一个区间,因为这个区间里的数都square root一下让区间和减小多少是不好说的。对于这种题就是要多想想,想到一个性质然后就能做了。关键点在于一个数square root最多7下就变成1了,而1以后就不影响了。所以更新的时候判断下当前区间是否都是1,如果都是1直接return(都是1的话就是区间和等于区间长度);不然的话不管它是不是终止结点(直到叶子结点为止)都继续更新下去。也就是说前几次都是O(n)代价更新,那为什么不会超时呢?因为当更新次数足够多以后,再更新的时候就会发现当前区间都是1,那么就自动变成logn更新了!O(n)线性更新的总代价应该是7*n级别的,完全可以过。
坑点在输入要是lld,我就是输入成了int,然后爆成负数,之后负数做sqrt还是返回负数,所以一直都是O(n)更新,因此竟然很多次都是tle而不是wa,给了我错误导向。。还有是两艘ship输入不保证大小关系,所以可能要swap一下
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define MAXN 200010 #define maxnode 100000 #define ll long long #define lowbit(x) (x&(-x)) using namespace std; ll a[MAXN]; struct node{ int l,r; ll d,tag;//endurance & if all is 1 node *ls,*rs; }pool[4*MAXN]; int top; node* buildT(int l,int r){ node* p = pool + (++top); p->l = l; p->r = r; p->tag = 0; if( l==r ){ p->d = a[l]; if( a[l]==1 ) p->tag=1; return p; } int mid = (l+r)>>1; p->ls = buildT(l,mid); p->rs = buildT(mid+1,r); p->d = p->ls->d + p->rs->d; p->tag = (p->ls->tag&&p->rs->tag); return p; } void update(node* p,int l,int r){ if( p->l==l && p->r==r && p->tag ){//all is one return; } if(p->l==p->r){//leaves p->d = long( sqrt(p->d) ); if( p->d==1 ) p->tag=1; return; } int mid = (p->l+p->r)>>1; if( r<=mid ) update(p->ls,l,r); else if( l>mid ) update(p->rs,l,r); else{ update(p->ls,l,mid); update(p->rs,mid+1,r); } p->d = p->ls->d + p->rs->d; p->tag = p->ls->tag && p->rs->tag; return; } ll query(node* p,int l,int r){ if( p->l==l && p->r==r ) return p->d; int mid = (p->l+p->r)>>1; if( r<=mid ) return query(p->ls,l,r); else if( l>mid ) return query(p->rs,l,r); return query(p->ls,l,mid)+query(p->rs,mid+1,r); } int main(){ //ios::sync_with_stdio(false); int n,tc=0; while( scanf("%d",&n)!=EOF ){ printf("Case #%d:\n",++tc); for(int i=1;i<=n;i++) scanf("%lld",a+i); int m; scanf("%d",&m); top=0; node* root = buildT(1,n); for(int i=1;i<=m;i++){ int t,x,y; scanf("%d%d%d",&t,&x,&y); if( x>y ) swap(x,y); if( t==0 ) update(root,x,y); else printf("%lld\n",query(root,x,y)); } printf("\n"); } return 0; }