ATB136F Enclosed Points 学习笔记
ATB136F Enclosed Points 学习笔记
题意简述
平面上有 \(n\) 个初始点(均为整点),定义一个点集的权值为平面上包含这个点集的最小矩形所包含的初始点个数(矩形边与坐标轴平行)。求所有非空点集的权值和。保证每个点的横纵坐标都不相同。
\(1\le n\le 2\times 10^5\)。
做法解析
一个暴力是枚举所有点集一个一个算。这样太SB了,因为点集的信息量是 \(O(2^n)\) 的。\(2\times 10^5\) 的数据范围决定了我们要对 \(O(N)\) 的对象统计贡献,所以我们来考虑:每个点被包含在了多少矩形里面?
设我们当前在考虑的点为 \(u\)。如果 \(u\) 在点集里面,它也一定在矩形里面,这一步的方案数是 \(2^{n-1}\) 的。如果 \(u\) 不在点集里面,什么矩形可以框到它呢?要么同时选了 \(u\) 左上和右下的点,要么同时选了 \(u\) 左下和右上的点。更形式化地,我们以 \(u\) 为原点建平面直角坐标系,设第 \(i\) 象限的点数为 \(c_i\),则不选 \(u\) 时其被包含的方案数就是 \(A+B-C,A=(2^{c_1}-1)(2^{c_3}-1)2^{c_2+c_4},B=(2^{c_2}-1)(2^{c_4}-1)2^{c_1+c_3}\)。还要减去一个 \(C=(2^{c_1}-1)(2^{c_3}-1)(2^{c_2}-1)(2^{c_4}-1)\) 是为了容斥掉四个象限都有选点的情况。
代码实现上,我们一开始先把所有点自身被选时的贡献加上,即 \(n2^{n-1}\)。然后把 \(y_i\) 离散化,开两个树状数组,一个存 \(x_i<x_u\) 的点的 \(y_i\),一个存 \(x_i>x_u\) 的点的 \(y_i\)。一开始把点都加进后者,然后顺着扫一遍。没有代码难度。
代码实现
#include <bits/stdc++.h>
using namespace std;
namespace obasic{
typedef long long lolo;
template <typename _T>
void readi(_T &x){
_T k=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
x*=k;return;
}
template <typename _T>
void writi(_T x){
if(x<0)putchar('-'),x=-x;
if(x>9)writi(x/10);
putchar(x%10+'0');
}
template <typename _T>
int lwberi(_T carr[],int alen,_T val){return lower_bound(carr+1,carr+alen+1,val)-carr;}
};
using namespace obasic;
const int MaxN=2e5+5,Mod=998244353;
int N,H[MaxN],nln;lolo pw2[MaxN],ans;
struct bpoi{int x,y;}P[MaxN];
bool cmpx(bpoi a,bpoi b){return a.x<b.x;}
struct BinidTree{
int n,t[MaxN];
void init(int x){n=x,fill(t,t+n,0);}
int lowbit(int x){return x&(-x);}
void add(int p,int x){for(;p<=n;p+=lowbit(p))t[p]+=x;}
int gts(int p){int res=0;for(;p;res+=t[p],p-=lowbit(p));return res;}
}BiT1,BiT2;
int main(){
readi(N);pw2[0]=1;BiT1.init(N),BiT2.init(N);
for(int i=1;i<=N;i++)pw2[i]=pw2[i-1]*2%Mod;
ans=N*pw2[N-1]%Mod;
for(int i=1;i<=N;i++)readi(P[i].x),readi(P[i].y),H[i]=P[i].y;
sort(H+1,H+N+1);nln=unique(H+1,H+N+1)-(H+1);
sort(P+1,P+N+1,cmpx);
for(int i=1;i<=N;i++)P[i].y=lwberi(H,N,P[i].y);
for(int i=1;i<=N;i++)BiT2.add(P[i].y,1);
for(int i=1;i<=N;i++){
BiT2.add(P[i].y,-1);
int c1=(N-i)-BiT2.gts(P[i].y);
int c2=(i-1)-BiT1.gts(P[i].y);
int c3=BiT1.gts(P[i].y-1);
int c4=BiT2.gts(P[i].y-1);
lolo A=1ll*(pw2[c1]-1)*(pw2[c3]-1)%Mod*pw2[c2+c4]%Mod;
lolo B=1ll*(pw2[c2]-1)*(pw2[c4]-1)%Mod*pw2[c1+c3]%Mod;
lolo C=1ll*(pw2[c1]-1)*(pw2[c3]-1)%Mod*(pw2[c2]-1)%Mod*(pw2[c4]-1)%Mod;
(ans+=A+B-C+Mod)%=Mod,BiT1.add(P[i].y,1);
}
writi(ans);
return 0;
}
反思总结
懂得对 \(O(N)\) 的对象展开统计,考虑它们每个能被多少/什么包含,造成贡献当且仅当什么。