AtCoder Beginner Contest 321题解(C,F,G)

ABC321 题解(C,F,G)

C 321-like Searcher

题意:一个数字串,如果从高位到低位严格递减则被称为“321串”(特例:不包括0和1),问第k大的“321串”是什么。

解答:只要从0-9中选定了不同的k个数,由于要满足严格递减,那么相应的“321串”也就唯一确定。所以“321串”总个数为 2 10 − 2 = 1022 2^{10}-2=1022 2102=1022个。枚举不同选择方法即可。

F #(subset sum = K) with Add and Erase

题意:给定一个集合,往集合中不断增删元素,问每次操作后和为K的子集的数量。

解答:如果只有增加元素,这是一道简单的DP。令 d p i dp_i dpi表示和为 i i i的子集数量,每次增加元素 k k k,则转移方程为 d p i = d p i + d p i − k dp_i=dp_i+dp_{i-k} dpi=dpi+dpik,每次倒序遍历。删除元素就是增加元素的逆,转移方程 d p i = d p i − d p i − k dp_i=dp_i-dp_{i-k} dpi=dpidpik,注意每次正序遍历,为什么是这样?

我们可以画出状态空间转移的图示:

在这里插入图片描述

如图所示,一个元素回溯转移的过程会发现它实际上是三角元素和方块元素的和,由于我们想要消去+2的操作,方块元素是我们不希望转移到它身上的,我们想要减去方块元素,而方块元素会转移到 d p i − 2 dp_{i-2} dpi2上,所以我们只要让 d p i = d p i − d p i − k dp_i=dp_i-dp_{i-k} dpi=dpidpik就可以了,注意一定要正序遍历 i i i

G Electric Circuit

题意:N个元件,每个元件上面都有一些A口和B口,保证所有元件总共的A口和B口数目相同,均为M,现在随意用M根电线连接任意A口和B口(同一个元件上A口和B口也可以相连),问连完后形成的连通分量个数的数学期望。

解答:对N个元件的子集 S ⊆ N S\subseteq N SN,定义 g ( S ) g(S) g(S)是让S内元件自成一个连通分量的连接方案数。

易知 g ( S ) = 0 g(S)=0 g(S)=0若S为空集或S内元件A口数和B口数不相同(这样的话S内元件一定得连向外部,不能自成一个连通分量),否则设S内A口数量为K, g ( S ) = k ! − ∏ { s 1 , s 2 , . . . , s t } g ( s i ) ( t > 1 ) g(S)=k!-\prod_{\{s_1,s_2,...,s_t\}}g(s_i)(t>1) g(S)=k!{s1,s2,...,st}g(si)(t>1)其中 { s 1 , s 2 , . . . , s t } \{s_1,s_2,...,s_t\} {s1,s2,...,st}是S的一个划分,如何快速的算出 g ( S ) g(S) g(S)?我们可以通过固定S中一个元素,枚举它所在的连通分量算出来。用数位DP,从1遍历到 2 N 2^N 2N,我们可以依次算出 g ( S ) g(S) g(S),每算出一个 g ( S ) g(S) g(S),可以看出它对所有方案的总连通分量个数的贡献为 g ( S ) ∗ ( M − k ) ! g(S)*(M-k)! g(S)(Mk)!,这样当然会有重复计数,但是n个连通分量的连接方案就会计数n次,正是我们想要的。

最后将所有方案总连通分量数除以 M ! M! M!得到答案

代码(模版省略):

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
int main(){
    int n,m;
    cin>>n>>m;
    vector<int> red(n,0),blue(n,0);
    vector<int> rcnt(1<<n,0),bcnt(1<<n,0);
    for(int i=0;i<m;i++){
        int r;
        cin>>r;
        r--;
        red[r]++;
    }
    for(int i=0;i<m;i++){
        int b;
        cin>>b;
        b--;
        blue[b]++;
    }
    for(int i=0;i<(1<<n);i++){
        for(int j=0;j<n;j++){
            if(i>>j&1){
                rcnt[i]+=red[j];
                bcnt[i]+=blue[j]; //记录每一个元件子集S有多少A口和B口
            }
        }
    }
    vector<Z> fac(m+1,0);
    fac[0]=1;
    for(int i=1;i<=m;i++){
        fac[i]=fac[i-1]*i;
    }
    vector<Z> dp(1<<n,0),f(1<<n,0),g(1<<n,0);
    Z ans=0;
    for(int bit=1;bit<(1<<n);bit++){
        if(rcnt[bit]!=bcnt[bit]) continue;
        f[bit]=g[bit]=fac[rcnt[bit]];
        for(int sub=(bit-1)&bit;sub>bit-sub;sub=(sub-1)&bit){ //枚举最高位在哪个连通分量中
            g[bit]-=g[sub]*f[bit-sub];
        }
        ans+=g[bit]*fac[m-rcnt[bit]];
    }
    ans/=fac[m];
    cout<<ans<<'\n';
    return 0;
}

时间复杂度的分析有一点困难,易知是最后的二层for循环贡献了主要的复杂度,而最内层代码执行次数为 ( n 1 ) 2 0 + ( n 2 ) 2 1 + . . . + ( n n ) 2 n − 1 = ( 1 + 2 ) n 2 = 3 n 2 \binom{n}{1}2^0+\binom{n}{2}2^1+...+\binom{n}{n}2^{n-1}=\frac{(1+2)^n}{2}=\frac{3^n}{2} (1n)20+(2n)21+...+(nn)2n1=2(1+2)n=23n所以复杂度为 O ( 3 N ) O(3^N) O(3N),考虑到 N ≤ 17 N\leq 17 N17,这样的复杂度是可以接受的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值