P2789

直线交点数

看到 $n$ 这么小,想必多项式复杂度的做法是不太现实的,那我们想一些暴力的非多项式做法。

其实这个交点的情况和平行的线有关。

如果说有 $m$ 组线,第 $i$ 组有 $a_i$ 条线,这 $a_i$ 条线相互平行。

那么我们的答案非常好解决:

$$Ans=\sum_{i=1}^ma_i(n-\sum_{j=1}^ia_j)$$

这个显然是可以 $O(n)$ 计算出来的。

那么我们考虑如何枚举序列 ${a_m}$。

比如说我们对于第 $i$ 条直线,我们可以考虑它放到前一组直线还是放到后一组直线。

然后就可以了。

因为每条直线只有两种选择,所以最后的复杂度是 $O(2^n)$ 的。

然后总的时间复杂度是 $O(n2^n)$。

考虑到这个序列实际上是组合,我们可以尝试使 $a_i\ge a_{i-1}$ 来剪枝。

然后更优的做法是 $O(n^4)$ 的魔幻 DP。

我们定义 $f(i,j)$ 表示 $i$ 条线交出 $j$ 个交点是否有可能。

那么我们可以有转移 $f(i,k+(i-j)*j)|=f(j,k)$。

这里可以用 bitset 优化。

但是据说还有复杂度更优的做法,但是我不会。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

const ll N=1e3;

ll n,ans,m;

ll a[N+5];

bool f[N+5];

void dfs(ll step) {
if(step>n) {
ll sum=0,tmp=0;
for(ll i=1;i<=m;i++) {
tmp+=a[i];
sum+=a[i]*(n-tmp);
}
if(!f[sum]) {f[sum]=1;ans++;}
return;
}
a[m]++;dfs(step+1);a[m]--;
if(n-step+1<a[m]) return;
m++;a[m]=1;
dfs(step+1);
a[m]=0;m--;
}

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}

void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}

int main() {

n=read();

m++;a[m]=1;
dfs(2);

write(ans);

return 0;
}

代码(DP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<bitset>
#define ll long long
using namespace std;

const ll N=700,M=5e5;

ll n,ans;

bitset<M> f[N];

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}

void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}

int main() {

n=read();

for(ll i=1;i<=n;i++) f[i][0]=1;

for(ll i=2;i<=n;i++) {
for(ll j=0;j<=i;j++) {
f[i]|=f[j]<<((i-j)*j);
}
}

write(f[n].count());

return 0;
}