[CQOI2010] 内部白点
题意:
一个无穷大的网格图,初始时有n个黑点。每一个时刻,若点$(x,y)$满足:
- $\exist x1<x<x2$使得$(x1,y),(x2,y)$都是黑点。
- $\exist y1<x<y2$使得$(x,y1),(x,y2)$都是黑点。
则点$(x,y)$也会变成黑点,求最终黑点数目,若一直变化输出-1。
$n\leq 10^{5},|x|,|y|\leq 10^9$。
题解:
一开始把题里这两个条件读成“或者”而不是“而且”了,导致不会做,然后重新读了一遍发现是sb题。
注意到一行或一列只有极大/极小值有用,而新加的黑点肯定不会是极大/极小值,那么实际上只会加一次或者一次都不加。
然后就是个弱化版二维偏序,直接树状数组即可。
复杂度$O(n\log{n})$。
套路:
- 维护不了多次变化的题$\rightarrow$考虑一下是不是只能变一次的诈骗题。
代码:
#include<bits/stdc++.h> #define maxn 500005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; struct node{int x,y;}P[maxn]; int B[maxn],C[maxn],vis[maxn]; vector<int> L[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline int lowbit(int x){return x&(-x);} inline void add(int x,int y,int n){for(int i=x;i<=n;i+=lowbit(i))C[i]+=y;} inline int qry(int x){int r=0;for(int i=x;i;i-=lowbit(i))r+=C[i];return r;} int main(){ int n=read(),tot=0; for(int i=1;i<=n;i++){ P[i].x=read(),P[i].y=read(); B[++tot]=P[i].x,B[++tot]=P[i].y; } sort(B+1,B+1+tot); for(int i=1;i<=n;i++){ P[i].x=lower_bound(B+1,B+1+tot,P[i].x)-B; P[i].y=lower_bound(B+1,B+1+tot,P[i].y)-B; L[P[i].x].push_back(P[i].y); //cout<<P[i].x<<" "<<P[i].y<<endl; } int ans=0; for(int x=1;x<=tot;x++){ if(!L[x].size()) continue; sort(L[x].begin(),L[x].end()); for(int j=0;j<L[x].size();j++){ int y=L[x][j],res=qry(y); if(vis[y]) ans+=res; else vis[y]=1; add(y,-res,tot),add(y+1,res,tot); } for(int j=0;j<L[x].size()-1;j++){ int y1=L[x][j]+1,y2=L[x][j+1]-1; if(y1<=y2) add(y1,1,tot),add(y2+1,-1,tot); } } printf("%d\n",ans+n); return 0; }