[AHOI2018初中组]球球的排列
IX.[AHOI2018初中组]球球的排列
论DP的百种用法之一
因为DP必须有一种全面的状态,但是这道题……似乎排列等等问题都不是DP擅长处理的地方。
首先分析性质。我们发现,这种不能放在一起的关系具有传递性。因为如果,那么。
具有传递性的话,我们就会发现,所有不能放在一起的位置,构成了多个团(完全图)。
我们就想着把每个团里的所有球都染上同一种颜色,则相同颜色的球不能紧贴在一起。
则我们现在将问题转换为:给你个染了色的球,相同的球不能放一起,求排列数。
考虑将这些球按照颜色排序,这样便有了一个合理的(可以抽象出状态的)DP顺序。
我们设表示:
当前DP到第位,
有两个球放在一起,它们的颜色相同,并且颜色与第位的球不同,这样的对共有个,
有两个球放在一起,它们的颜色相同,并且颜色与第位的球相同,这样的对共有个,
的方案数。
因为我们已经排过序,因此颜色相同的球必定紧贴。
1.第 位的球和第 位的球颜色不同。
则DP状态的第三维(即)必为,因为不存在在它之前并且和它颜色相同的球。我们只需要枚举第二维。
1.1.我们将这个球放在两个颜色不同的球之间。
我们枚举一个,表示上一个球所代表的颜色中颜色相同且紧贴的对共有个()。
则有f[i][j][0]+=f[i-1][j-k'][k']*(i-j)
,因为共有j-k'
个相邻且相同且和上一个球的颜色不同的位置,并且共有i-j
个可以放球的位置。
1.2.我们将这个球放在两个颜色相同的球之间。
我们仍然枚举一个,意义相同。这时,。
则有f[i][j][0]+=f[i-1][j-k'+1][k']*(j+1)
。因为放入这个球后就拆开了一对球,因此原来共有 对这样的球。
2.第 位的球和第 位的球颜色相同。
我们需要枚举剩余两维。并且,设在第位之前,有个和第位相同的位置。
2.1.我们将这个球放在某个和这个球颜色相同的球旁边。
则共有个这样的位置。
因此有f[i][j][k]+=f[i-1][j][k-1]*(2*cnt-(k-1))
。
2.2.我们将这个球放在两个颜色相同的球之间。
同1.2,有f[i][j][k]+=f[i-1][j+1][k]*(j+1)
。
2.3.我们将这个球放在两个颜色不同且与这个球颜色不同的球之间。
这次操作没有添加或删除任何对,并且共有个位置。
因此有f[i][j][k]+=f[i-1][j][k]*(i-(cnt*2-k)-j)
。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
int n,num[310],dsu[310],f[2][310][310];
vector<int>v;
int find(int x){
return dsu[x]==x?x:dsu[x]=find(dsu[x]);
}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return;
dsu[x]=y;
}
bool che(ll ip){
ll tmp=sqrt(ip)+0.5;
return tmp*tmp==ip;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&num[i]),dsu[i]=i;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(che(1ll*num[i]*num[j]))merge(i,j);
for(int i=1;i<=n;i++)dsu[i]=find(i);
sort(dsu+1,dsu+n+1);
f[0][0][0]=1;
for(int i=1,cnt;i<=n;i++){
memset(f[i&1],0,sizeof(f[i&1]));
if(dsu[i]!=dsu[i-1]){
cnt=0;
for(int j=0;j<i;j++){
for(int k=0;k<=j;k++)f[i&1][j][0]=(1ll*f[!(i&1)][j-k][k]*(i-j)+f[i&1][j][0])%mod;//if we put it between two balls of different colours
for(int k=0;k<=j+1;k++)f[i&1][j][0]=(1ll*f[!(i&1)][j-k+1][k]*(j+1)+f[i&1][j][0])%mod;//if we put it between two balls of the same colours
}
}else{
for(int j=0;j<i;j++){
for(int k=1;k<=cnt;k++)f[i&1][j][k]=(1ll*f[!(i&1)][j][k-1]*(cnt*2-(k-1))+f[i&1][j][k])%mod;//if we put it next to a ball of the same colour
for(int k=0;k<=cnt;k++)f[i&1][j][k]=(1ll*f[!(i&1)][j+1][k]*(j+1)+f[i&1][j][k])%mod;//if we put it between two balls of the same colours
for(int k=0;k<=cnt;k++)f[i&1][j][k]=(1ll*f[!(i&1)][j][k]*(i-(cnt*2-k)-j)+f[i&1][j][k])%mod;//if we put it between two balls of different colours
}
}
cnt++;
}
printf("%d\n",f[n&1][0][0]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?