首页 > 其他分享 >社论 22.10.9

社论 22.10.9

时间:2022-10-09 19:14:34浏览次数:80  
标签:prime tmp 社论 int rep cnt times 22.10

CF840C

给定一个序列 \(a\),长度为 \(n\)。试求有多少 \(1\) 到 \(n\) 的排列 \(p_i\),满足对于任意的 \(2\le i\le n\) 有 \(a_{p_{i-1}}\times a_{p_i}\) 不为完全平方数,答案对 \(10^9+7\) 取模。

\(n \le 300,a_i \le 10^9\).

连续……不是很连续 段dp。

首先考虑什么时候有限制“\(a_{p_{i-1}}\times a_{p_i}\) 为完全平方数”。
我们设 \(a^2 = b\times c,b = p\times x^2, c =q\times y^2\),其中 \(p,q\) 无平方因子。则有 $ p\times q$ = \left(\frac a {xy}\right)^2$。由于 \(p,q\) 无平方因子,因此若 \(p\times q\) 为完全平方数,则唯一的可能就是 \(p=q\)。
因此我们预先筛出 \(\le \sqrt n\) 的质数,把每个数的平方因子除去,问题就转化成了不能有相邻两个数相同的排列数量。

考虑一个插入dp。
枚举当前插入的数是哪个数,再枚举插入的位置,就可以开始分类讨论了。

  1. 插入数和一侧的数相同
  2. 插入数和两侧的数相同
  3. 插入数和两侧的数都不同,两侧的数彼此不同
  4. 插入数和两侧的数都不同,两侧的数彼此相同

然后开始设dp数组。
设 \(f[i][j][k]\) 为插入了 \(i\) 个数,已经有 \(j\) 对相同的数,\(k\) 对是由和当前插入数相同的数产生的的方案数。
设当前数插入前已经插入了 \(x\) 个和当前数相同的数,开始转移:

  1. \(f[i][j][k] = f[i[j][k] + f[i-1][j-1][k-1] \times (2(x - 2(k-1)) + 2(k-1))\)
  2. \(f[i][j][k] = f[i[j][k] + f[i-1][j-1][k-1] \times (k-1)\)
  3. \(f[i][j][k] = f[i[j][k] + f[i-1][j][k] \times (i-(2x-k)-(j-k))\)
  4. \(f[i][j][k] = f[i[j][k] + f[i-1][j+1][k] \times (j-k+1)\)

然后转移就完了。记得压一维,但不压似乎也没问题。
时间复杂度 \(O(n^3)\)。

code
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define rep(i, a, b) for (register int(i) = (a); (i) <= (b); ++(i))
#define pre(i, a, b) for (register int(i) = (a); (i) >= (b); --(i))
const int N = 3e2 + 10;
int n, a[N], tmp, mod = 1e9 + 7, f[2][N][N];

int prime[1000005], cnt;
bool vis[1000005];
void sieve(int bnd) {
    rep(i,2,bnd) {
        if (!vis[i]) prime[++cnt] = i;
        rep(j,1,cnt) {
            if (i * prime[j] > bnd) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> n; rep(i,1,n) cin >> a[i], tmp = max(tmp, a[i]);
    sieve(sqrt(tmp) + 10); 
    rep(i,1,cnt) prime[i] = prime[i] * prime[i];
    rep(i,1,n) rep(j,1,cnt) while (a[i] % prime[j] == 0) a[i] /= prime[j];
    sort(a+1, a+1+n);
    f[0][0][0] = 1; tmp = 0;
    rep(i,1,n) {
        int ptr = i & 1, ztr = ptr ^ 1;
        memset(f[ptr], 0, sizeof f[ptr]);
        if (a[i] != a[i-1]) {
            rep(j,0,i) {
                rep(k,1,tmp) {
                    f[ztr][j][0] = (f[ztr][j][k] + f[ztr][j][0]) % mod;
                    f[ztr][j][k] = 0;
                }
            }
            tmp = 0;
        }
        rep(j,0,i) rep(k,0,min(tmp,j)) {
            if (j > 0 and k > 0) f[ptr][j][k] = (f[ptr][j][k] + 1ll * f[ztr][j-1][k-1] * ((tmp << 1) - k + 1)) % mod;
            f[ptr][j][k] = (f[ptr][j][k] + 1ll * f[ztr][j+1][k] * (j + 1 - k)) % mod;
            f[ptr][j][k] = (f[ptr][j][k] + 1ll * f[ztr][j][k] * (i - ((tmp << 1) - k) - (j - k))) % mod;
        } tmp++;
    } cout << f[n & 1][0][0] << endl;
}

[能不能再给力一点啊?]

题面相同。

\(n \le 5000,a_i \le 10^9\).

容斥题。

为方便,设 \(s_i\) 为除去平方因子后第 \(i\) 大的数出现了多少次。
为方便,首先进行无标号计数,最后乘入 \(\prod (s_i!)\) 即可。

于是问题转化成了有颜色无标号小球排列计数,需要相邻小球颜色不同。这就比较的典。
我们考虑一个容斥。首先设有 \(b\) 段长度大于 \(1\) 的相同颜色段,然后我们把这些段缩成一个小球。设第 \(i\) 种颜色被缩成的小球数是 \(t_i\)(\(\sum t_i = b\)),则有

\[ans = \sum_{b}(-1)^{b} \frac{(n-b)!\prod_{i}\binom{s_i-1}{t_i}}{\prod_{i}(s_i - t_i)!} \]

\((n-b)!\) 是当前缩完后的序列的总可能性,然后我们得除去那些已经在一个连续段内的排列情况。这是分子。分母考虑对连续段做一下插板法使得计数的序列都满足不会再缩起来的情况。

先不考虑 \((n-b)!\),最后得到答案时乘进去就行。套路地设 \(f_{i,j}\) 为前 \(i\) 个数值,\(b=j\) 时的答案。定义 \(n < m\) 时 \(\binom{n}{m} =0\),\(n < 0\) 时 \(n! = 0\),我们有转移

\[f_{i,j} = \sum_{k=0}^j f_{i-1,j-k} \times \frac{\binom{s_i-1}{k}}{(s_i - k)!} \]

记得得到答案时乘进去该乘的阶乘。

code
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define rep(i, a, b) for (register int(i) = (a); (i) <= (b); ++(i))
#define pre(i, a, b) for (register int(i) = (a); (i) >= (b); --(i))
const int N = 300 + 10;
int n, ptr, ztr, a[N], s[N], tmp, jc[N], inv[N], mod = 1e9 + 7, f[2][N];

int prime[1000005], cnt;
bool vis[1000005];
void sieve(int bnd) {
    rep(i,2,bnd) {
        if (!vis[i]) prime[++cnt] = i;
        rep(j,1,cnt) {
            if (i * prime[j] > bnd) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}

int qp(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    } return ret;
}
int C(int n, int m) { if (n < m) return 0; return 1ll * jc[n] * inv[m] % mod * inv[n-m] % mod; }

int main() {
    ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    cin >> n; rep(i,1,n) cin >> a[i], tmp = max(tmp, a[i]);

    sieve(sqrt(tmp) + 10); 
    rep(i,1,cnt) prime[i] = prime[i] * prime[i];
    rep(i,1,n) rep(j,1,cnt) while (a[i] % prime[j] == 0) a[i] /= prime[j];
    sort(a+1, a+1+n);
    tmp = 0; cnt = 0;
    
    jc[0] = inv[0] = 1;
    rep(i,1,n) jc[i] = 1ll * jc[i-1] * i % mod;
    inv[n] = qp(jc[n], mod - 2);
    pre(i,n-1,1) inv[i] = 1ll * inv[i+1] * (i+1) % mod;

    rep(i,1,n) if (a[i] != a[i-1]) ++ s[++cnt]; else ++ s[cnt];
    f[0][0] = 1;
    rep(i,1,cnt) {
        tmp += s[i] - 1;
        ptr = i & 1, ztr = ptr ^ 1;
        memset(f[ptr], 0, (tmp + 1) << 2);
        rep(j,0,tmp) {
            rep(k,0,min(s[i], j)) {
                f[ptr][j] = (f[ptr][j] + 1ll * f[ztr][j-k] * C(s[i] - 1, k) % mod * inv[s[i] - k]) % mod;
            }
        } 
    }

    int ans = 0; ptr = cnt & 1;
    rep(i,0,tmp) 
        if (i & 1) ans = (ans + 1ll * (mod - 1) * jc[n - i] % mod * f[ptr][i]) % mod;
        else ans = (ans + 1ll * jc[n - i] % mod * f[ptr][i]) % mod;
    rep(i,1,cnt) ans = 1ll * ans * jc[s[i]] % mod;
    cout << ans << endl;
}

[能不能再给力一点啊?]

题面相同。

\(n \le 10^5,a_i \le 10^9\).

考虑构造两个生成函数 \(F_k\) 与 \(G_k\)。

\[F_k(x) = \sum_{i=0}f_{k,i} x^i \]

\[G_k(x) = \sum_{i=0}^{s_k} \frac{\binom{s_k-1}{i}}{(s_k - i)!}x^i \]

则我们能发现 \(F_k = F_{k-1} \times G_k\)。而 \(F_0 = 1\)。
则我们有答案即为

\[\sum_{i=0}^{n-b}(-1)^{i}\times (n-i)! \times \left([x^i]\prod_{i=1}^bG_i\right) \]

通过分治NTT得到最右边那个多项式的系数,然后算出来就行了。注意最后乘进去一个阶乘。

[代码还没有,等我学会任意模数分治NTT再说]

标签:prime,tmp,社论,int,rep,cnt,times,22.10
From: https://www.cnblogs.com/joke3579/p/editorial221009.html

相关文章

  • 2022.10.9
    复盘小知识电脑睡眠与休眠睡眠休眠(与合上电脑一样)文件关闭与否关否恢复操作鼠标or任意键电源键适合情况......
  • 2022.10.6 总结
    C有一棵树,每次操作将一个点染成黑色,每次询问查询一个点最近的黑点有多远。有两种暴力:对于一个被修改为黑色的点,\(BFS\)给所有点更新。对于一个所求点,和所有黑色点求......
  • 【闲话】2022.10.08
    今日考试又寄了怎么凡是Accoders上的考试我都会寄啊今天Winter'SRain先生搞魔改猪国杀然后\(\color{red}{\查\抄\正\着\}\)实在是……太巨了今天挂一张......
  • 2022.10.8
    关于我电脑坏了:隔了好久没有写东西主要原因是电脑坏了电脑店老板给人感觉挺不错的,说自己也是网络工程,但是那个时候有学硬件方向他给我看了电脑,说是主板问题,他修不了,可......
  • 2022.10.7第三次组会记录
    团队:集农广益小组地点:桃园食堂时间:晚上九点参与人:全体人员组会内容摘要:分析项目具体架构和功能,讨论数据流图的设计要求组会主要内容:1.分析讨论用户的具体功能:发帖、......
  • 2022.10.7
    ACM。结果不是很好。一开始的节奏是很好的,但从A题调不出来开始就乱了。每个人再自己的题上都有深入思考,但对别人的情况不了解,所以讨论的效率实际不高,而且很容易被套进死胡......
  • 【闲话】2022.10.07
    发现似乎我妈登上博客园了那我是不是该收敛点啊总之今天考了场试啊密码还是我的某中文网名全拼。还是相对论:只要大家都挂了那我就没有挂————bikuhiku看......
  • 闲话 22.10.7
    闲话最近洛谷犇犇炸了诶所以没有地方推流了所以开摆了最近想用代数工业优化dp今天社论就是一道但是推广的时候就会遇到很多问题所以有什么偏难怪题让我切吗今天早......
  • Matlab function 2.22.10.07
    functiony=myfun1(x)z=x+1;y=z*2;functiony=myfun2(x)ifx>10y=99;elseifx>0y=1;elsey=-1;endfunctiony=myfun3(x)switchx......
  • 社论 22.10.7
    CF1603F给定三个正整数\(n,k,x\),求满足以下条件的序列\(a_{1...n}\)的个数:对于每一个\(1\leqslanti\leqslantn\),\(0\leqslanta_i<2^k\)。序列\(a\)没......