引
计数DP,好像特别经典,有两种做法,我只会 O ( n 3 ) O(n^3) O(n3),有 O ( n 2 ) O(n^2) O(n2)的
解法
首先,
若
x
y
=
p
2
且
x
z
=
q
2
,
则
y
z
=
(
p
q
x
)
2
若xy=p^2且xz=q^2,则yz=(\frac{pq}{x} )^2
若xy=p2且xz=q2,则yz=(xpq)2 所以题目中给出的关系具有传递性,故先预处理,不可相邻的球分在一个组
为了方便,下面叫同一组的球为同色
伪代码如下:
bool check(ll x){
ll tmp=sqrt(x);
return (tmp*tmp==x);
}
for(int i=1;i<=n;i++) {
a[i]=read(),b[i]=i;
for(int j=1;j<i;j++) {
if(check(1ll*a[i]*a[j])) {b[i]=j;break;}
}
}
接下来,
设计状态
首先我们无脑枚举一维
i
i
i表示前
i
i
i个球,注意同色的球排序后的序列是连续的
观察题目的限制相当于同色的球不能相邻,设计后两维要考虑好同色和相邻
f
i
,
j
,
.
k
:
前
i
个球,共有
j
+
k
对相邻的球同色,其中
j
对跟第
i
个球异色,
k
对同色
f_{i,j,.k}:前i个球,共有j+k对相邻的球同色,其中j对跟第i个球异色,k对同色
fi,j,.k:前i个球,共有j+k对相邻的球同色,其中j对跟第i个球异色,k对同色
那么最后的答案
a
n
s
=
f
n
,
0
,
0
ans=f_{n,0,0}
ans=fn,0,0
设计出了状态其实就成功了一半,但这道题的转移也很毒瘤,客官且看
状态转移
考虑
f
i
f_i
fi与
f
i
−
1
f_{i-1}
fi−1的关系,
f
i
f_i
fi的状态都是由
i
−
1
i-1
i−1个球的
i
i
i个空隙插入一个球而来的
那么第
i
i
i个球与第
i
−
1
i-1
i−1个球有两种关系:a.同色;b.异色
第
i
i
i个球插入的位置也有两种可能:c.插入两个同色球之间;d.插入两个异色球之间
所以对6种可能分别考虑转移:
1. a与c
发现,此时前
i
−
1
i-1
i−1个球已有与
i
i
i同色的,那么明显第
i
i
i个球放在与其同色的球旁边转移不同,设排列中已有
c
n
t
cnt
cnt个球与
i
i
i同色,此时有:
f
i
,
j
,
k
=
f
i
−
1
,
j
,
k
−
1
∗
(
c
n
t
∗
2
−
(
k
−
1
)
)
f_{i,j,k}=f_{i-1,j,k-1}*(cnt*2-(k-1))
fi,j,k=fi−1,j,k−1∗(cnt∗2−(k−1))
然后第
i
i
i个球插入的位置是与自己异色的球间,让
j
j
j减少了1,此时有:
f
i
,
j
,
k
=
f
i
−
1
,
j
+
1
,
k
∗
(
j
+
1
)
f_{i,j,k}=f_{i-1,j+1,k}*(j+1)
fi,j,k=fi−1,j+1,k∗(j+1)
2.a与d
减去前两种情况,还剩
i
−
c
n
t
∗
2
+
k
−
j
i-cnt*2+k-j
i−cnt∗2+k−j种情况,
j
,
k
j,k
j,k不变,此时有:
f
i
,
j
,
k
=
f
i
−
1
,
j
,
k
∗
(
i
−
c
n
t
∗
2
+
k
−
j
)
f_{i,j,k}=f_{i-1,j,k}*(i-cnt*2+k-j)
fi,j,k=fi−1,j,k∗(i−cnt∗2+k−j)
3.b与c
可知第
i
i
i个球的颜色是首次出现,故
k
k
k=0,让
(
j
+
k
)
(j+k)
(j+k)减少了1,所以要枚举
j
‘
j‘
j‘和
k
’
k’
k’,有:
f
i
,
j
,
0
=
∑
j
′
+
k
′
=
j
+
1
f
i
,
j
′
,
k
′
∗
(
j
+
1
)
f_{i,j,0}=\sum_{j'+k'=j+1} f_{i,j',k'} *(j+1)
fi,j,0=j′+k′=j+1∑fi,j′,k′∗(j+1)
4.b与d
对
j
,
k
j,k
j,k不产生影响,枚举即可,此时有:
f
i
,
j
,
0
=
∑
j
′
+
k
′
=
j
f
i
,
j
′
,
k
′
∗
(
i
−
j
)
f_{i,j,0}=\sum_{j'+k'=j} f_{i,j',k'}*(i-j)
fi,j,0=j′+k′=j∑fi,j′,k′∗(i−j)
代码实现倒是简单,放一下
code:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 3e2 + 5,mod=1e9+7;
int n,cnt;
int f[2][N][N],a[N],b[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
bool check(ll x){
ll tmp=sqrt(x);
return (tmp*tmp==x);
}
int main() {
n=read();
for(int i=1;i<=n;i++) {
a[i]=read(),b[i]=i;
for(int j=1;j<i;j++) {
if(check(1ll*a[i]*a[j])) {b[i]=j;break;}
}
}
sort(b+1,b+1+n);
f[0][0][0]=1;
for(int i=1;i<=n;i++,cnt++) {
int now=i&1,pre=now^1;
memset(f[now],0,sizeof f[now]);
if(b[i]==b[i-1]){
for(int j=0;j<i;j++) {
for(int k=0;k<=cnt;k++){
if(k) f[now][j][k]=(1ll*f[now][j][k]+1ll*f[pre][j][k-1]*(cnt*2-(k-1)))%mod;
f[now][j][k]=(1ll*f[now][j][k]+1ll*f[pre][j+1][k]*(j+1))%mod;
f[now][j][k]=(1ll*f[now][j][k]+1ll*f[pre][j][k]*(i-cnt*2+k-j))%mod;
}
}
}else {
cnt=0;
for(int j=0;j<i;j++){
for(int k=0;k<=j+1;k++){
if(k<=j) f[now][j][0]=(1ll*f[now][j][0]+1ll*f[pre][k][j-k]*(i-j))%mod;
f[now][j][0]=(1ll*f[now][j][0]+1ll*f[pre][k][(j+1)-k]*(j+1))%mod;
}
}
}
}
printf("%d\n",f[n&1][0][0]);
}
文章讲述了使用计数动态规划解决一个关于球的颜色组合问题,涉及状态转移和判断球是否同色的函数,主要关注O(n^3)和O(n^2)的时间复杂度优化。
440

被折叠的 条评论
为什么被折叠?



