球球的排列

文章讲述了使用计数动态规划解决一个关于球的颜色组合问题,涉及状态转移和判断球是否同色的函数,主要关注O(n^3)和O(n^2)的时间复杂度优化。

题目传送门

计数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=p2xz=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} fi1的关系, f i f_i fi的状态都是由 i − 1 i-1 i1个球的 i i i个空隙插入一个球而来的
那么第 i i i个球与第 i − 1 i-1 i1个球有两种关系:a.同色;b.异色
i i i个球插入的位置也有两种可能:c.插入两个同色球之间;d.插入两个异色球之间
所以对6种可能分别考虑转移:

1. a与c

发现,此时前 i − 1 i-1 i1个球已有与 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=fi1,j,k1cnt2(k1)
然后第 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=fi1,j+1,k(j+1)

2.a与d

减去前两种情况,还剩 i − c n t ∗ 2 + k − j i-cnt*2+k-j icnt2+kj种情况, 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=fi1,j,k(icnt2+kj)

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+1fi,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=jfi,j,k(ij)

代码实现倒是简单,放一下

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]);
}

TXL

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值