首页 技术 正文
技术 2022年11月15日
0 收藏 715 点赞 2,378 浏览 2562 个字

传送门

题目大意:

给出平面上的 $n$ 个点,每个点有唯一的标号(label),这 $n$ 个标号的集合记作 $S$,点可能重合。求满足下列条件的 $S$ 的子集 $T$ 的数目:

1. $|T|\ge 2$

2. $T$中的点共线

Solution:

只包含一种点的符合条件的子集 $T$ 的数目很容易计算,以下我们只考虑其中至少有两种不同(指坐标不同,下同)点的符合条件的子集$T$(以下简称子集)的数目。

考虑过两不同点 $u, v$ 的直线上的点,将这些点的集合记作 $P_{uv}$,考虑 $P_{uv}$ 的子集对答案的贡献 $C_{u,v}$ 。

设 $|P_{uv}|=k$,则 $C_{uv}=2^k-k-1-\text{single}(P_{uv})$,其中 $\text{single}(P_{uv})$ 指 $P_{uv}$ 的只包含一种点的子集 $T$ 的数目。

现在固定 $u$,计算一共能得到几个 $P_{uv}$。这可通过以 $u$ 为基点,对(去重后的)其他点作极角排序来做到。

以每个点为基点做一次极角排序,这样每个点集 $P_{uv}$ 都被重复计算了 $|P_{uv}|$ 次。

我们用 $f[k]$ 表示模为 $k$ 的点集 $P_k$ 出现的次数。这个次数被重复计算了$k$次,$P_k$出现的次数为$\frac{f[k]}{k}$

则所有$P_k$贡献的子集的数目为

\[\frac{f[k]}{k}(2^k-k-1)-\sum\limits_{P_k}\text{single}(P_k),\]

故总计数为

\[\sum_{k=2}^{n}{\frac{f[k]}{k} 2^k-(k+1)} -\sum_{k=2}^{n}{\sum\limits_{P_k}\text{single}(P_k)}\]

为了计算$\sum_{k=2}^{n}{\sum\limits_{P_k}\text{single}(P_k)}$,另外维护一个数组 $d$,$d[u]$ 表示 $u$ 在多少个点集 $P$ 中出现过。

从而有

\[\sum_{k=2}^{n}{\sum_{P_k}\text{single}(P_k)}=\sum_{u=1}^{m}d[u]*(2^{c[u]}-c[u]-1),\]

其中 $m$ 指去重后的点数,$c[u]$ 表示与 $u$ 重合的点数。

Implement:

#include <bits/stdc++.h>
using namespace std;const int N(1e3+);
typedef long long LL;
LL mod=1e9+;struct point{
LL x, y;
void read(){
scanf("%lld%lld", &x, &y);
}
point operator-(const point &b)const{
return {x-b.x, y-b.y};
}
point operator-(){
return {-x, -y};
}
bool operator==(const point &b)const{
return x==b.x && y==b.y;
}
bool operator<(const point &b)const{
return x==b.x?y<b.y:x<b.x;
}
void out(){
cout<<x<<' '<<y<<endl;
}
LL operator^(const point &p)const{
return x*p.y-y*p.x;
}
}a[N], slope[N];bool cmp(const int &a, const int &b){
return (slope[a]^slope[b])<; //error-prone
}int cnt[N], b[N], dup[N];LL num[N], p[N]={};int main(){ int T, n;
for(int i=; i<=; i++) p[i]=(p[i-]<<)%mod; for(scanf("%d", &T); T--; ){
scanf("%d", &n);
for(int i=; i<n; i++)
a[i].read(); sort(a, a+n);
for(int i=, j=, k=; i<n; j++, k=i){
for(; i<n && a[i]==a[k]; i++);
cnt[j]=i-k;
} int e=unique(a, a+n)-a; memset(num, , sizeof(num));
memset(dup, , sizeof(dup)); for(int i=; i<e; i++){ for(int j=, k=; j<e; j++){
if(i==j) continue;
slope[j]=a[j]-a[i];
if(slope[j].x<) slope[j]=-slope[j];
else if(slope[j].x== && slope[j].y<) slope[j]=-slope[j];
b[k++]=j;
} sort(b, b+e-, cmp); for(int j=, k=, sum; j<e-; k=j){
for(sum=; j<e- && (slope[b[j]]^slope[b[k]])==; sum+=cnt[b[j++]]);
num[sum+cnt[i]]+=cnt[i]; //error-prone
dup[i]++;
}
} LL _dup=, res=; for(int i=; i<e; i++) _dup+=dup[i]*(p[cnt[i]]-cnt[i]-+mod), _dup%=mod;
for(int i=; i<e; i++) res+=p[cnt[i]]-cnt[i]-+mod, res%=mod;
for(int i=; i<=n; i++)
num[i]/=i, res+=num[i]*(p[i]-i-+mod), res%=mod;
res+=mod-_dup, res%=mod;
printf("%lld\n", res);
}
}

UPD

在以某个点为基点进行极角排序需要进行一个将待排序的向量规范化(formalization)的过程, 具体的说就是保证各向量满足:

x>0 或 x==0 && y>0 (待排序都是非零向量)

题解上给出的做法更好一些:

想将输入的点去重后,按照字典序排序, 然后从左到右扫, 当扫到第$i$个点$p_i$时我们统计由$p_i$及其右面的点构成的且一定包含$p_i$的共线子集的数目, 统计的方法和前面类似.

这样做的好处是

1. 向量自然规范化

2. 不重不漏

Implementation:

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,105
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,582
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,429
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,200
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,836
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,919