网址:http://codeforces.com/problemset/problem/1190/D
题意:
给出平面直角坐标系下的点,使用一个上不封顶的矩形把某些点围住作为一个集合,两个集合不同只要数量不同或者数量相同时存在一个点不同,集合必须非空,求这样的集合的数量。
题解:
扫描线法:对$y$轴自顶向下,对于其中一层$y_i$,有以下$n$个点,坐标是$x_1,x_2,x_3......x_n$,以递增序排列,在某一时候,$x_i$加入序列,$y_i$层之前已经计数的矩形不受影响,增加的是加入了新的点之后可构成的新矩形。所以以这个点作为新点,统计加入前的矩形。为了保证有边界,左边界取$1$,有边界取$x$轴的最大值$+1$。则由计数原理,不同的矩形只要存在一条边界不同即可,点数$=$边数$-1$,故矩形数$=$这个点(左边点数$+1)$$*$$($右边点数$+1)$。由于同一个横坐标的值的点等价,对于纵坐标更小而横坐标相同的点,因为已经计算它上面的点,所以其不能和纵坐标大的点累加,故扫描时直接覆盖而不是累加。
然后是技巧,这个题的的数据范围是$2e5$,所以需要数据结构优化,然后坐标值范围是$1e9$,所以需要离散化。
AC代码:
(线段树)
#include <iostream> #include <algorithm> #include <cmath> #include <cstring> #pragma GCC optimize(2) using namespace std; struct Point { int x,y; bool operator<(const Point &a)const { return y==a.y?x<a.x:y>a.y; } }; Point p[200005]; int x[200005],y[2000005]; struct Segtree { struct node { int l,r; int v; }tr[200005*4]; void build(int l,int r,int k) { tr[k].l=l,tr[k].r=r; if(l==r) { tr[k].v=0; return; } int m=(l+r)/2; build(l,m,k*2); build(m+1,r,k*2+1); tr[k].v=tr[k*2].v+tr[k*2+1].v; } void update(int pos,int k,int val)//单点修改 { if(tr[k].l==tr[k].r) { tr[k].v=val; return; } int m=(tr[k].l+tr[k].r)/2; if(pos<=m) update(pos,k*2,val); else if(pos>m) update(pos,k*2+1,val); tr[k].v=tr[k*2].v+tr[k*2+1].v; } int query(int l,int r,int k) { if(l>r) return 0; if(l<=tr[k].l&&r>=tr[k].r) { return tr[k].v; } int m=(tr[k].l+tr[k].r)/2; //cout<<tr[k].l<<" "<<tr[k].r<<endl; int ans=0; if(l<=m) ans+=query(l,r,k*2); if(r>m) ans+=query(l,r,k*2+1); return ans; } }; Segtree seg; int ydiv[200005]; int main() { ios::sync_with_stdio(0); cin.tie(0); int n; cin>>n; for(int i=0;i<n;++i) { cin>>x[i]>>y[i]; p[i].x=x[i]; p[i].y=y[i]; } sort(x,x+n); sort(y,y+n); int xnew=unique(x,x+n)-x; int ynew=unique(y,y+n)-y; for(int i=0;i<n;++i) { p[i].x=lower_bound(x,x+xnew,p[i].x)-x+1; p[i].y=lower_bound(y,y+ynew,p[i].y)-y+1; //cout<<p[i].x<<" "<<p[i].y<<endl; } sort(p,p+n); //扫描线 seg.build(1,xnew+1,1); int beg=0,cur=0; long long sum=0; while(beg<n) { int pos=0; while(cur<n&&p[cur].y==p[beg].y) { seg.update(p[cur].x,1,1); ydiv[pos++]=p[cur++].x; } ydiv[pos]=xnew+1; for(int i=0;i<pos;++i) { long long cntl=seg.query(1,ydiv[i]-1,1); long long cntr=seg.query(ydiv[i]+1,ydiv[i+1]-1,1); sum+=(cntl+1)*(cntr+1); } beg=cur; } cout<<sum<<endl; return 0; }
(树状数组)
注:单点修改时,因为每个横坐标值最多只会同时有一个点(被覆盖),所以已经修改成$1$的地方,再需要修改时,也是覆盖成1,所以直接用一个$vis$数组记录是否已经修改。
#include <iostream> #include <algorithm> #include <cstring> #pragma GCC optimize(2) using namespace std; struct Fenwick { bool cntx[200005]; int c[200005]; int lowbit(int x) { return x&(-x); } void update(int pos,int val,int n)//Containing error { if(cntx[pos]) return; else { cntx[pos]=1; for(int i=pos;i<=n;i+=lowbit(i)) c[i]+=val; } } int sum(int pos,int n) { int sum=0; for(int i=pos;i;i-=lowbit(i)) sum+=c[i]; return sum; } int query(int l,int r,int n) { if(l>r) return 0; return sum(r,n)-sum(l-1,n); } void print(int n) { for(int i=0;i<n;++i) cout<<c[i]<<" "; cout<<endl; } }; struct Point { int x,y; bool operator<(const Point &a)const { return y==a.y?x<a.x:y>a.y; } }; Point p[200005]; int x[200005],y[200005],ydiv[200005]; Fenwick tr; int main() { ios::sync_with_stdio(0); cin.tie(0); int n; cin>>n; for(int i=0;i<n;++i) { cin>>x[i]>>y[i]; p[i].x=x[i]; p[i].y=y[i]; } sort(x,x+n); sort(y,y+n); int xn=unique(x,x+n)-x; int yn=unique(y,y+n)-y; for(int i=0;i<n;++i) { p[i].x=lower_bound(x,x+xn,p[i].x)-x+1; p[i].y=lower_bound(y,y+yn,p[i].y)-y+1; //cout<<p[i].x<<" "<<p[i].y<<endl; } sort(p,p+n); //扫描线 memset(tr.c,0,sizeof(tr.c)); int beg=0,cur=0; //max xn+1 long long sum=0; while(beg<n) { int pos=0; while(cur<n&&p[cur].y==p[beg].y) { tr.update(p[cur].x,1,xn+1); ydiv[pos++]=p[cur++].x; //tr.print(xn+1); } ydiv[pos]=xn+1; for(int i=0;i<pos;++i) { long long cntl=tr.query(1,ydiv[i]-1,xn+1); long long cntr=tr.query(ydiv[i]+1,ydiv[i+1]-1,xn+1); sum+=(cntl+1)*(cntr+1); } beg=cur; } cout<<sum<<endl; return 0; }