题解:洛谷 P10142([USACO24JAN] Mooball Teams III P)
1. Description
有 \(n\) 个点,现在需要在所有点中选出一个子集,并且将这个子集分成两个不交的非空集合,称为红队和蓝队,要求满足存在一条水平或竖直的直线可以将红队和蓝队分开,问选取红队和蓝队的方案数。
2. Solution
注:下文假定奶牛在一个平面直角坐标系中,\(x\) 坐标的正方向为右侧,\(y\) 坐标的正方向为上方。
首先,红队和蓝队没有本质区别,所以我们考虑求出其中一种然后乘二就是答案。
考虑容斥,求出可以被水平直线分开的方案数和可以被竖直直线分开的方案数之和,然后减掉既可以被水平直线分开,又可以被竖直直线分开的方案数。
可以被水平直线分开的方案数和可以被竖直直线分开的方案数之和很好求。
首先考虑可以被水平直线分开的方案数,枚举分界线 \(mid\),指定 \(x=mid\) 的奶牛一定在红队,且\(x\in[1,mid-1]\) 的奶牛要么不选,要么在红队,\(x\in [mid+1,n]\) 的奶牛要么不选,要么在蓝队中。
显然,两部分奶牛分别有 \(mid-1\) 和 \(n-mid\) 头。那么红队的选择方案就有 \(2^{mid-1}\) 种,蓝队的选择方案有 \(2^{n-mid}-1\) 种,这里红队无需考虑空集问题,但是蓝队需要考虑,那么一共有 \(2^{mid-1}\times (2^{n-mid}-1)=2^{n-1}-2^{mid-1}\) 种情况,直接求解即可。
而可以被竖直直线分开的方案数和可以被水平直线分开的方案数相等,可以直接乘二。
现在的问题是怎么求解可以同时被水平直线和竖直直线分开的方案数,有一个 naive 的想法,就是直接枚举水平和竖直的直线,然后红队在左上角中选,蓝队在右下角中选;红队在右上角中选,蓝队在左下角中选,同时,直线上的点一定在红队中,时间复杂度为 \(O(n^2)\),显然需要优化。
我们不妨枚举竖直直线 \(x\),同时钦定在竖直直线上的奶牛一定在红队中,那么根据上面的算法,假定竖直直线上的奶牛 \(y\) 坐标为 \(now\),我们可以将此时的方案数分为两种:\(x\in [now+1,n],y\in [now+1,n]\) 的只能为红,\(x\in [1,now-1],y\in [1,now-1]\) 的奶牛中右红,左蓝;\(x\in [1,now-1],y\in [now+1,n]\) 的只能为红,\(x\in [now+1,n],y\in [1,now-1]\) 的奶牛中左红,右蓝,显然可以使用线段树加扫描线维护,节点中保存只选红色的方案数,只选蓝色的方案数,左红右蓝的方案数,左蓝右红的方案数即可。
3. Code
/*by qwer6*/
/*略去缺省源和快读快写*/
const int mod=1e9+7,N=2e5+5;
int n,ans;
int y[N],pw[N];
struct Node{
int R,B,rb,br;
//这里的方案数均计算空集的情况
Node(int _R=0,int _B=0,int _rb=0,int _br=0){
R=_R,B=_B,rb=_rb,br=_br;
}
Node friend operator +(Node x,Node y){
Node res;
res.R=mul(x.R,y.R),res.B=mul(x.B,y.B);
res.rb=sub(add(mul(x.rb,y.B),mul(x.R,y.rb)),mul(x.R,y.B));
//这里是因为左边只有红色且右边只有蓝色的方案数算了两次
//为了防止重复计算,所以减掉,下一行同
res.br=sub(add(mul(x.br,y.R),mul(x.B,y.br)),mul(x.B,y.R));
return res;
}
};
struct Segment_tree{
Node c[N<<2];
#define ls p<<1
#define rs p<<1|1
#define mid (l+r>>1)
void pushup(int p){
c[p]=c[ls]+c[rs];
}
void build(int p,int l,int r){
if(l==r){
c[p]=Node(1,2,2,2);
//注意初始值的设置,需要计算有空集的情况
return ;
}
build(ls,l,mid),build(rs,mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int x){
if(l==r){
c[p]=Node(2,1,2,2);
return ;
}
if(mid>=x)change(ls,l,mid,x);
else change(rs,mid+1,r,x);
pushup(p);
}
Node query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return c[p];
if(mid>=L&&mid<R)return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
if(mid>=L)return query(ls,l,mid,L,R);
return query(rs,mid+1,r,L,R);
}
}Set;
int add(int x,int y){
x+=y;
return x>=mod?x-mod:x;
}
int sub(int x,int y){
x-=y;
return x<0?x+mod:x;
}
int mul(int x,int y){
long long res=1ll*x*y;
return res>=mod?res%mod:res;
}
signed main(){
read(n);
pw[0]=1;
for(int i=1,_x,_y;i<=n;i++){
read(_x),read(_y);
y[_x]=_y;
pw[i]=mul(pw[i-1],2);
}
for(int i=1;i<=n;i++)ans=add(ans,mul(pw[i-1],sub(pw[n-i],1)));
ans=mul(ans,2);
Set.build(1,1,n);
for(int i=1;i<=n;i++){
Set.change(1,1,n,y[i]);
Node L,R;
if(1<=y[i]-1)L=Set.query(1,1,n,1,y[i]-1);
else L=Node(1,1,1,1);
if(y[i]+1<=n)R=Set.query(1,1,n,y[i]+1,n);
else R=Node(1,1,1,1);
//去掉蓝队为空的情况!
ans=sub(ans,mul(L.R,sub(R.rb,R.R)));
ans=sub(ans,mul(sub(L.br,L.R),R.R));
}
write(mul(ans,2));
}

浙公网安备 33010602011771号