\(\rm GYM\)

题目描述

有一个大小为 \(n\) 的数可重集 \(w\),你需要将其分成 \(k\) 组,每组至少有一个数。对于一个大小为 \(s\),包含数字之和为 \(t\) 的分组,它的权值是 \(s\times t\)

每种分组方案的权值就是每个分组的总和。请求出所有分组方案的权值和取模 \(998244353\) 下的结果。

\(n\le 10^6\)

解法

就觉得... 自己挺傻的。以下是自己的考场思路:

枚举长度 \(i\),计算任意能源在长度 \(i\) 中的方案数 —— \(n-i\) 个能源分成 \(k-1\) 组方案数 × 从 \(n-1\) 中选 \(i-1\) 个 × 所有能量值之和 × \(i\)

好耶ヽ(✿゚▽゚)ノ把样例凑出来了!

考虑将 \(n\) 个元素分成 \(m\) 组。若知晓了每组的大小 \(\{s\},\sum s=n\),此时就是 \(\frac{n!}{s_1!s_2!...s_m!}\)

\(dp_{i,j,k}\)\(i\) 个元素分成 \(j\) 组,最后一组长度为 \(k\) 的方案数,而且保证长度递增。因为我不会去重

\[dp_{i,j,k}=dp_{i-k,j-1,q}\cdot\frac{i!}{(i-k)!}\cdot \frac{1}{k!}(q\le k) \]

哦嚯,后面就是个组合数,最重要的是和 \(q\) 无关。所以搞个前缀和?希望不要假了。只有 \(\text{40 pts}\)?怎么旁边都在说卷积?日你吗,这个不是 \(\mathcal{O}(n^3)\) 的吗。

但是其实是不是除以 \(j!\) 就可以去重了?可是状态数就已经死了。

\[dp_{i,j}=dp_{k,j-1}\cdot \frac{i!}{k!\cdot(i-k)!} \]

这个柿子可以卷积?每个式子变化量不同,咋维护啊?可是写 \(\mathcal{O}(n^3)\) 不就和指数级暴力同分了吗?

出题人谢谢你😅。

考完之后身边的 \(\rm ET\) 就对我说将 \(n\) 个互异元素分成 \(m\) 组就是第二类斯特林数!递推也非常简单可以做到 \(\mathcal O(n^2)\)

\[\text{S}(i,j)=\text{S}(i-1,j-1)+(j-1)\cdot \text{S}(i-1,j) \]

心情真的坏透了,大家都会容斥

事实上,第二类斯特林数可以用容斥原理来计算,先给出结果:

\[\text{S}(n,k)=\frac{1}{k!}\cdot \sum_{i=0}^k (-1)^i \cdot \binom{k}{i}\cdot (k-i)^n \]

我们不妨先钦定盒子是互异的,最后除以阶乘即可。令 \(A_i\)\(n\) 个互异球放进 \(k\) 个盒子,第 \(i\) 个盒子为空的方案集合,那么我们需要求的就是 \(|\bigcap_{i=1}^k A_i^c|\)\(c\) 符号表示补集)。

这样就可以祭出容斥原理:

\[|A_1^c\cap A_2^c\cap ...\cap A_n^c|=|S|-\sum\limits_{1\le i\le n}|A_i|+\sum\limits_{1\le i<j\le n}|A_i\cap A_j|-...+(-1)^n\times |A_1\cap A_2\cap ...\cap A_n| \]

\(f(t)\) 表示 \(\sum_{1\le i_1<i_2<...<i_t\le n}|A_{i_1}\cap ...\cap A_{i_t}|\)。注意到,\(A_i\) 的定义并没有保证其它盒子不为空,所以钦定一个下标集时,答案就是 \((k-t)^n\)。钦定下标集是 \(\binom{k}{t}\) 的。左边就是 \(\text{S}(n,k)\cdot k!\),所以原式得证。

不过按我的做法还是需要计算 \(n\) 个斯特林数,复杂度并没有优化。

有一个思路上特别妙的转化:我们并不一定需要知道一个数处在分组的大小,当数字 \(x\) 和另一个数字 \(y\) 处于一个分组时,分组权值就会增加 \(x+y\)!所以答案还可以这样描述(就是将两个数字捆绑在一起了):

\[\text{Ans}=\sum w_i\cdot \text{S}(n,k)+\sum_{i\neq j} (w_i+w_j)\cdot \text{S}(n-1,k)\\ =\sum w_i \cdot (\text{S}(n,k)+(n-1)\cdot \text{S}(n-1,k)) \]

对于 \((k-i)^n\) 用欧拉筛一下就可以做到 \(\mathcal{O}(n)\) 了。

七负我

题目描述

青山七海是打工狂魔。

她计划拿出 \(x\) 小时进行打工,共有 \(n\) 家店铺可以去,她需要将这 \(x\) 小时的打工时间分配到这些店铺。

\(n\) 家店铺的基础工资是一样的,但青山七海了解到,它们之间有 \(m\) 对合作伙伴关系。若店铺 \(u, v\) 是合作伙伴,并且青山七海在 \(u, v\) 的打工时间分别为 \(t_u, t_v\),就能额外获得 \(t_u ×t_v\) 的工资。

青山七海想知道,在恰当分配这些打工时间的情况下,她最多能获得多少额外工资呢?

一个店铺被分配到的打工时间可以为任意非负实数,即包括 \(0\)

\(1\le n\le 40,1\le x\le 100\)

解法

\(u,v\) 之间没有边,记 \(s_u,s_v\) 分别表示与 \(u,v\) 相连的点的 \(t\) 之和。那么 \(u,v\) 对答案的贡献就是 \((s_u\cdot t_u+s_v\cdot t_v)\),当 \(t_u+t_v\) 为定值 \(S\) 时,这个柿子就是 \((s_u-s_v)\cdot t_u+s_v\cdot S\)。你会发现当 \(s_u-s_v>0\) 时,柿子在 \(t_u=S\) 时取最大值;反之,在 \(t_u=0\) 时取最大值 —— 也就是说,最优情况一定是 \(t_u,t_v\) 中有一个为零。

那么最优解一定是 \(t>0\) 的点形成一个团(团内部的点两两有边)。

考虑贡献的形式是两两匹配,其实就是 \((t_1+t_2+...+t_k)^2\) 中的所有交叉项之和,也可以被表示成 \(\frac{\left(\sum_{i=1}^k t_i \right)^2-\sum_{i=1}^k t_i^2}{2}\)。前面一坨就是 \(x^2\),后面一坨取最小值时就是令 \(t_i=\frac{x}{k}\)。所以我们得出了已知团的大小得到最大答案的构造方案。

假设团大小为 \(k\),答案就是 \(\frac{k(k-1)\cdot x^2}{2k^2}\)。显然地,当 \(k\) 越大值就越大,所以只需要找到图上的最大团即可。

具体有两种方法:

\(\text{Meet in the middle}\)

将点分成两半。对于前一半求出 \(f(S)\) 表示点集为 \(S\) 的导出子图的最大团;对于后一半,枚举点集 \(P\),找到 \(N(P)\) 与前一半点的交集 \(V\),答案就是 \(|P|+f(V)\)。时间复杂度 \(\mathcal O(n\cdot 2^{n/2})\)

\(\text{Bron–Kerbosch}\) 算法

时间复杂度是 \(\mathcal O(3^{n/3})\) 的,但我怎么可能会证明呢?

代码

/*
cnt_i:用 [i,n] 之间的点能构成的最大团。用于可行性剪枝。
now:现在尝试拓展第 now 个点
*/
bool dfs(int x,int now) {
    for(int i=x+1;i<=n;++i) {
        if(cnt[i]+now-1<=ans) return 0;
        if(!e[x][i]) continue;
        for(int j=1;j<=now;++j) 
            if(j==now) {
                a[now]=i;
                if(dfs(i,now+1))
                    return 1;
            }
            else if(!e[i][a[j]]) break;
    }
    if(now-1>ans) return ans=now-1,1;
    return 0;
}

for(int i=n;i>=1;--i) {
    a[1]=i;
    dfs(i,2);
    cnt[i]=ans;
}
posted on 2021-10-31 15:54  Oxide  阅读(58)  评论(0编辑  收藏  举报