Codeforces 题目传送门 & 洛谷题目传送门

这是一道 *2500 的 D1C,可个人认为难度堪比某些 *2700 *2800。

不过嘛,*2500 终究还是 *2500,还是被我自己想出来了

双倍经验 P4448 [AHOI2018初中组]球球的排列 哦

u1s1 其实一年以前做过 P4448,不过好像直到我 AC 这道题之后才发现这俩题一模一样,并且似乎这次用的方法和上次还不太一样

跑题了跑题了

首先有个性质:\(\forall a,b,c\in\mathbb{N}^+\) 若 \(ab,bc\) 均为完全平方数,\(ac\) 也是完全平方数,因为假设 \(ab=x^2,bc=y^2\),则 \(ac=\dfrac{a^2b^2c^2}{ab·bc}=(\dfrac{abc}{xy})^2\),也就是说 \(ac\) 是一个有理数的平方,而又显然 \(ac\in\mathbb{N}^+\),故 \(ac\) 为完全平方数。

考虑 \(\forall i,j\in[1,n],i\ne j\),若 \(a_ia_j\) 为完全平方数,就在 \(i,j\) 之间连一条边,那么根据之前的推论,若 \((i,j),(j,k)\) 之间均存在边,那么 \((i,k)\) 间也存在边,也就是说得到的图是一个个团的并集

考虑将同一个团中的点染上同一个颜色,于是原题等价于给出 \(k\) 种颜色,第 \(i\) 种颜色的球有 \(c_i\) 个(每种颜色的球互不相同),其中 \(\sum\limits_{i=1}^kc_i=n\),求将这 \(n\) 个球排成一行,其中相邻两个球颜色不同的方案数。

考虑 \(dp\),\(dp_{i,j}\) 表示将前 \(i\) 种颜色的球排成一行,其中有 \(j\) 个相邻位置颜色相同的方案数。

考虑从 \(dp_i\) 转移到 \(dp_{i+1}\),我们枚举第 \(i+1\) 种颜色分成了多少组,设为 \(x\),再枚举这 \(x\) 组中有多少组摆在了原本颜色相同的两球之间,设为 \(y\),那么这样一来会少掉 \(y\) 个颜色相同的相邻位置,但同时由于 \(i+1\) 种颜色分为了 \(x\) 组,又会多出 \(c_{i+1}-x\) 对颜色相同的相邻位置。算下这样划分的方案数,设 \(s_{i,j}\) 表示将 \(i\) 个不同的球划分成 \(j\) 组,组内有序但组与组之间无序的方案数,那么将第 \(i+1\) 种颜色的球划分为 \(x\) 组的方案数为 \(s_{c_{i+1},x}\),从 \(x\) 组中选出 \(y\) 组的方案数为 \(\dbinom{x}{y}\),这 \(y\) 组进一步“消灭”颜色相同的位置的方案数为 \(k^{\underline{y}}\),将剩余的 \(x-y\) 组填入剩余的 \((\sum\limits_{t=1}^ic_t)+1\) 个空隙中的方案数为 \(((\sum\limits_{t=1}^ic_t)+1)^{\underline{x-y}}\),故我们有状态转移方程式 \(dp_{i,j}\times s_{c_{i+1},x}\times \dbinom{x}{y}\times k^{\underline{y}}\times ((\sum\limits_{t=1}^ic_t)+1)^{\underline{x-y}}\rightarrow dp_{i+1}{j-y+c_{i+1}-x}\)。最终答案即为 \(dp_{k,0}\)。

最后考虑怎样求 \(s_{i,j}\),考虑最后一个球的摆法,若它单独成一组,则 \(dp_{i,j}\leftarrow dp_{i-1,j-1}\),若它与前面的求合并为一组,则它有 \(i-1+j\) 个空隙可以插入,\(dp_{i,j}\leftarrow dp_{i-1,j}\times(i+1-j)\)。

算下时间复杂度,乍一看四重循环,\(n^4\),但实际上 \(\sum\limits_{i=1}^kc_i=n\),故复杂度是 \(n^3\) 的,可以通过此题。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=300;
const int MOD=1e9+7;
int n,a[MAXN+5],f[MAXN+5];
bool issqr(ll x){int _sqrt=sqrt(x);return (1ll*_sqrt*_sqrt==x);}
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);if(x^y) f[x]=y;}
int siz[MAXN+5],b[MAXN+5],bn=0,s[MAXN+5][MAXN+5];
int dp[MAXN+5][MAXN+5],fac[MAXN+5],ifac[MAXN+5];
void init_fac(int n){
fac[0]=ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;
for(int i=1;i<=n;i++) ifac[i]=1ll*ifac[i]*ifac[i-1]%MOD;
s[0][0]=1;
for(int i=1;i<=n;i++) for(int j=1;j<=i;j++){
s[i][j]=(s[i-1][j-1]+1ll*(i-1+j)*s[i-1][j])%MOD;
}
}
int binom(int x,int y){
if(x<0||y<0||x<y) return 0;
return 1ll*fac[x]*ifac[x-y]%MOD*ifac[y]%MOD;
}
int main(){
scanf("%d",&n);init_fac(n+1);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(issqr(1ll*a[i]*a[j]))
merge(i,j);
for(int i=1;i<=n;i++) siz[find(i)]++;
for(int i=1;i<=n;i++) if(find(i)==i) b[++bn]=siz[i];
// for(int i=1;i<=bn;i++) printf("%d\n",b[i]);
dp[0][0]=1;int sum=0;
for(int i=0;i<bn;i++){
for(int j=0;j<=sum-i;j++){
for(int k=1;k<=b[i+1];k++){
int ways=s[b[i+1]][k];
for(int l=0;l<=min(j,k);l++){
int ways2=1ll*binom(k,l)*fac[l]%MOD*binom(j,l)%MOD;
ways2=1ll*ways2*binom(sum+1-j,k-l)%MOD*fac[k-l]%MOD;
ways2=1ll*ways2*ways%MOD;ways2=1ll*dp[i][j]*ways2%MOD;
// printf("%d %d %d %d %d\n",i,j,k,l,ways2);
dp[i+1][j-l+b[i+1]-k]=(dp[i+1][j-l+b[i+1]-k]+ways2)%MOD;
}
}
}
sum+=b[i+1];
}
// for(int i=1;i<=bn;i++) for(int j=0;j<=n;j++) printf("%d %d %d\n",i,j,dp[i][j]);
printf("%d\n",dp[bn][0]);
return 0;
}

事实上本题还有更优秀的做法

下面就是我没想到的部分了

我们考虑将每种颜色定义一个 \(b_i\) 表示将这种颜色的球强制分为 \(b_i\) 组并要求同一组的球必须绑在一起。

考虑对于固定的 \(b_1,b_2,\dots,b_k\) 怎样计算这样填的方案数,首先对于每种颜色,将其分为 \(b_i\) 组的方案数为 \(c_i!\dbinom{c_i-1}{b_i-1}\times\),其次将这捆绑后的 \(B=\sum\limits_{i=1}^kb_i\) 组排列起来的方案数为 \(\dfrac{B!}{\prod\limits_{i=1}^kb_i!}\),故总方案数为 \(\prod\limits_{i=1}^k(c_i!\dbinom{c_i-1}{b_i-1})·\dfrac{B!}{\prod\limits_{i=1}^kb_i!}=B!\prod\limits_{i=1}^kc_i!·\prod\limits_{i=1}^k\dbinom{c_i-1}{b_i-1}\dfrac{1}{b_i!}\),我们考虑枚举 \(B\),那显然前两项都是常数,只需考虑第二个 \(\prod\) 中的东西就行了。

还是考虑 \(dp\),\(dp_{i,j}\) 表示考虑到前 \(i\) 个球,它们的 \(b_i\) 加起来等于 \(j\) 的 \(\prod\limits_{i=1}^k\dbinom{c_i-1}{b_i-1}\dfrac{1}{b_i!}\) 的和。

转移就枚举 \(b_i=l\),那么显然有 \(dp_{i,j}=\sum dp_{i-1,j-l}\dbinom{c_i-1}{l-1}\dfrac{1}{l!}\)。

那么最终 \(\sum\limits_{i=1}^k=B\) 的答案即为 \(dp_{k,B}\)。

但是这样计算有个问题就是会重复计算,比方说排列方式为 1 1 2 2,那么它在 \(dp_{2,3},dp_{2,4}\) 中都会被计算,不过这个问题很容易解决,直接容斥原理一下就行了,故最终答案为 \(\sum\limits_{i=k}^ndp_{k,B}\times(-1)^{n-i}\)。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=300;
const int MOD=1e9+7;
int n,a[MAXN+5],f[MAXN+5];
bool issqr(ll x){int _sqrt=sqrt(x);return (1ll*_sqrt*_sqrt==x);}
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);if(x^y) f[x]=y;}
int siz[MAXN+5],b[MAXN+5],bn=0;
int dp[MAXN+5][MAXN+5],fac[MAXN+5],ifac[MAXN+5];
void init_fac(int n){
fac[0]=ifac[0]=ifac[1]=1;
for(int i=2;i<=n;i++) ifac[i]=1ll*ifac[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;
for(int i=1;i<=n;i++) ifac[i]=1ll*ifac[i]*ifac[i-1]%MOD;
}
int binom(int x,int y){
if(x<0||y<0||x<y) return 0;
return 1ll*fac[x]*ifac[x-y]%MOD*ifac[y]%MOD;
}
int main(){
scanf("%d",&n);init_fac(n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(issqr(1ll*a[i]*a[j]))
merge(i,j);
for(int i=1;i<=n;i++) siz[find(i)]++;
for(int i=1;i<=n;i++) if(find(i)==i) b[++bn]=siz[i];
dp[0][0]=1;
for(int i=1;i<=bn;i++) for(int j=1;j<=n;j++) for(int k=1;k<=min(j,b[i]);k++){
dp[i][j]=(dp[i][j]+1ll*dp[i-1][j-k]*binom(b[i]-1,k-1)%MOD*ifac[k]%MOD)%MOD;
} int ans=0,mul=1;for(int i=1;i<=bn;i++) mul=1ll*mul*fac[b[i]]%MOD;
for(int i=bn;i<=n;i++){
if((n-i)&1) ans=(ans-1ll*dp[bn][i]*fac[i]%MOD*mul%MOD+MOD)%MOD;
else ans=(ans+1ll*dp[bn][i]*fac[i]%MOD*mul%MOD)%MOD;
} printf("%d\n",ans);
return 0;
}

Codeforces 840C - On the Bench(dp/容斥原理)的更多相关文章

  1. Codeforces 840C On the Bench dp

    On the Bench 两个数如果所有质因子的奇偶性相同则是同一个数,问题就变成了给你n个数, 相同数字不能相邻的方案数. dp[ i ][ j ]表示前 i 种数字已经处理完, 还有 j 个位置需 ...

  2. CodeForces 840C - On the Bench | Codeforces Round #429 (Div. 1)

    思路来自FXXL中的某个链接 /* CodeForces 840C - On the Bench [ DP ] | Codeforces Round #429 (Div. 1) 题意: 给出一个数组, ...

  3. codeforces 429 On the Bench dp+排列组合 限制相邻元素,求合法序列数。

    限制相邻元素,求合法序列数. /** 题目:On the Bench 链接:http://codeforces.com/problemset/problem/840/C 题意:求相邻的元素相乘不为平方 ...

  4. Codeforces 840C. On the Bench 动态规划 排列组合

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF840C.html 题解 首先,我们可以发现,如果把每一个数的平方因子都除掉,那么剩下的数,不相等的数都可以相 ...

  5. 【uoj#37/bzoj3812】[清华集训2014]主旋律 状压dp+容斥原理

    题目描述 求一张有向图的强连通生成子图的数目对 $10^9+7$ 取模的结果. 题解 状压dp+容斥原理 设 $f[i]$ 表示点集 $i$ 强连通生成子图的数目,容易想到使用总方案数 $2^{sum ...

  6. 【bzoj2560】串珠子 状压dp+容斥原理

    题目描述 有 $n$ 个点,点 $i$ 和点 $j$ 之间可以连 $0\sim c_{i,j}$ 条无向边.求连成一张无向连通图的方案数模 $10^9+7$ .两个方案不同,当且仅当:存在点对 $(i ...

  7. 【bzoj2339】[HNOI2011]卡农 dp+容斥原理

    题目描述 题解 dp+容斥原理 先考虑有序数列的个数,然后除以$m!$即为集合的个数. 设$f[i]$表示选出$i$个集合作为满足条件的有序数列的方案数. 直接求$f[i]$较为困难,考虑容斥,满足条 ...

  8. 洛谷 P2986 [USACO10MAR]Great Cow Gat…(树形dp+容斥原理)

    P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat… 题目描述 Bessie is planning the annual Great Cow Gathering for c ...

  9. [BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆)

    [BZOJ 3625] [Codeforces 438E] 小朋友的二叉树 (DP+生成函数+多项式开根+多项式求逆) 题面 一棵二叉树的所有点的点权都是给定的集合中的一个数. 让你求出1到m中所有权 ...

随机推荐

  1. .NET 分布式系统架构(有转载部分)

    一.设计目的 搭建一个大型平台需要综合考虑很多方面,不单纯是软件架构,还包括网络和硬件设备等.由于现代大部分应用建设都面临用户多.高并发.高可用的需求,传统软件架构已不能满足需求,需要支持分布式软件架 ...

  2. 240.搜索二维矩阵II

    从左下角位置开始搜索 时间复杂度:O(行数+列数). 想法有点像二分法,大了往一个方向找,小了往另一个方向找.由于矩阵横向和纵向都是递增,如果从(0,0)位置开始找,往右和往下都是增大,因此不知道实际 ...

  3. [no code][scrum meeting] Alpha 8

    项目 内容 会议时间 2020-04-14 会议主题 API文档第一版交付 会议时长 30min 参会人员 PM+OCR组成员 $( "#cnblogs_post_body" ). ...

  4. Noip模拟63 2021.9.27(考场惊现无限之环)

    T1 电压机制 把题目转化为找那些边只被奇数环包含. 这样的话直接$dfs$生成一棵树,给每个点附上一个深度,根据其他的非树边都是返祖边 可以算出环内边的数量$dep[x]-dep[y]+1$,然后判 ...

  5. caffe的idx1-ubyte和idx1-ubyte文件转换成图片文件和文本文件

    train-images-idx3-ubyte : training set images train-labels-idx1-ubyte   : training set labels t10k-i ...

  6. PCIE基本知识

    转载:https://zhuanlan.zhihu.com/p/139656925 前言 之前主要都在做FPGA算法层面的东西,最近觉得对于接口方面的知识比较欠缺,打算以PCI-E为例来系统的学习一下 ...

  7. analysis_screencap

    #!/usr/bin/env pythonfrom PIL import Imageimg = Image.open("./screen.png")maps = [[] for i ...

  8. DeWeb第2个通用化模块:主控模块。 手机/电脑自适应。通过修改配置文件即可实现进入不同模块

    演示: https://delphibbs.com/main.dw 也可以通过 https://delphibbs.com/login.dw 采用admin/123456登录后自动进入 开发环境和源代 ...

  9. 开源的分布式事务框架 springcloud Alibaba Seata 的搭建使用 一次把坑踩完。。。

    seata的使用 1. Seata 概述 Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而 ...

  10. xmind 文件 打开后会在当前目录生成 configuration,p2和workspace目录,artifacts.xml文件 解决

    在xmind安装目录下的xmind.ini修改如下配置,为绝对路径