【atcoder】Enclosed Points [abc136F]
题目传送门:https://atcoder.jp/contests/abc136/tasks/abc136_f
题目大意:在平面上有$n$个点我们,定义一个点集的权值为平面上包含这个点集的最小矩形所包含的点个数(矩形的边与坐标轴平行),求所有非空点集的权值和,保证每个点的横纵坐标互不相同。
先考虑转化一下,求每个点被多少个点集$S$的矩形包含,假设我们当前考虑的是点$i$,那么可以分成两种情况:$i \in S$或$i \notin S$。
1. 对于$i \in S$的情况,容易发现点$i$对所有包含$i$的点集有贡献,这里的贡献为$2^(n-1)$。
2. 对于$i \notin S$的情况,因为每个点的横纵坐标互不相同,所以点$i$把整个坐标系划分成了4个区域
那么若点集$S$的矩形包含点$i$,那么必存在$p,q \in S,p \in A,q \in D$或$p \in B,q \in D$。
设$A$区域中的点数量为$a$,$B$区域中的点数量为$b$,$C$区域中的点数量为$c$,$D$区域中的点数量为$d$,容斥可知这里的贡献为$(2^a-1)2^b2^c(2^d-1)+2^a(2^b-1)(2^c-1)2^d-(2^a-1)(2^b-1)(2^c-1)(2^d-1)$。
计算每个区域的点数可以将点排序离散化后用树状数组维护,于是就可以在$O(n \log n)$的时间复杂度下解决问题。
代码:
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define ll long long #define mod 998244353 #define maxn 200010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static char buf[20],len; len=0; if(x<0)x=-x,putchar('-'); for(;x;x/=10)buf[len++]=x%10+'0'; if(!len)putchar('0'); else while(len)putchar(buf[--len]); } inline void writesp(ll x){write(x); putchar(' ');} inline void writeln(ll x){write(x); putchar('\n');} struct Data{ int x,id; }num[maxn]; struct point{ int x,y,rk; }a[maxn]; int n; bool cmp1(Data a,Data b){return a.x<b.x;} bool cmp2(point a,point b){return a.x<b.x;} inline ll power(ll a,ll b) { ll ans=1; for(;b;b>>=1,a=a*a%mod) if(b&1)ans=ans*a%mod; return ans; } int bit1[maxn],bit2[maxn]; void add1(int x,int k){for(;x<=n;x+=x&(-x))bit1[x]+=k;} int getsum1(int x){int sum=0; for(;x;x-=x&(-x))sum+=bit1[x]; return sum;} void add2(int x,int k){for(;x<=n;x+=x&(-x))bit2[x]+=k;} int getsum2(int x){int sum=0; for(;x;x-=x&(-x))sum+=bit2[x]; return sum;} int main() { n=read(); for(int i=1;i<=n;i++){ a[i].x=read(); a[i].y=read(); num[i].x=a[i].y; num[i].id=i; } std::sort(num+1,num+n+1,cmp1); for(int i=1;i<=n;i++) a[num[i].id].rk=i; std::sort(a+1,a+n+1,cmp2); for(int i=1;i<=n;i++) bit1[i]=0,bit2[i]=i&(-i); ll ans=0; for(int i=1;i<=n;i++){ add2(a[i].rk,-1); int A=getsum1(a[i].rk),B=getsum1(n)-getsum1(a[i].rk),C=getsum2(a[i].rk),D=getsum2(n)-getsum2(a[i].rk); ll totA=power(2,A),totB=power(2,B),totC=power(2,C),totD=power(2,D); ans=(ans+(totA-1)*totB%mod*totC%mod*(totD-1))%mod; ans=(ans+totA*(totB-1)%mod*(totC-1)%mod*totD)%mod; ans=(ans-(totA-1)*(totB-1)%mod*(totC-1)%mod*(totD-1)%mod+mod)%mod; ans=(ans+power(2,n-1))%mod; add1(a[i].rk,1); } writeln(ans); return 0; }