zoukankan      html  css  js  c++  java
  • 模拟赛(一)T117903 防疫工作安排

    2020.1.30

    模拟赛(一)

    肝一下午和一晚上,绝对是到目前为止博客里最难的一道题!

    T2 防疫工作安排

    题目描述

    爆发肺炎疫情的H省共有n个地级市,为了最大限度减缓疾病蔓延,这些地级市用1到n开始编号,由n−1条道路相连,并且保证联通。统筹疫情防控的省会W市为根节点,编号为1

    为了防疫,首先需要给每个地级市安排一个重要度。具体来说,对于每个市,它的重要度是1,m中的一个整数(可以重复)。同时有一个特殊的要求:对于从根节点到叶子节点的每一条路径,路径上所有数字的最大公约数必须为1

    现在你需要求出合法的重要度安排方法的数量。答案对 1e9+7取模。

    输入格式

    第一行两个整数n,m,意义如题所示。

    接下来n−1行,每行两个整数u,v,表示一条道路。

    输出格式

    一行一个整数,表示答案。

    输入输出样例

    输入

    3 2
    1 2
    1 3
    

    输出

    5
    

    说明/提示

    【样例解释】

    1、2、3 号结点分别可以是 [1,2,2],[1,1,2],[1,2,1],[1,1,1],[2,1,1],一共 5 种方案。

    【数据范围与约定】

    对于40%的数据,满足1≤n≤100

    对于额外20%的数据,满足1≤m≤5

    对于额外20%的数据,满足树成为一条链。

    对于100%的数据,满足1≤n≤10^5^,1≤m≤20

    题解

    因为n个点由n-1条路连通,所以不难发现这是一棵树。根节点是1(省会)

    我们要保证从根到叶的每一条路径上至少有两数互质。

    因为m很小,只有20,那么就可以从分析m入手:

    两数互质,也就是两数没有相同质因数,20以下的数可能有的质因数有8个:2,3,5,7,11,13,17,19

    而且20以下的数至多有2个质因数(2·3·5=30>20)

    那么只要判断这八个质因数的有无,就能判断两数是否互质了

    为了表示质因数的有无,就要用2进制表示:00001001(9)就表示2,7两个质数

    开一个数组f存每个数的质因数,如f~12~=00000110

    设dp~i,j~表示(以j为根的子树的所有根叶路径)满足(i表示的质数至少有一次没出现)

    那么最终答案就是dp~255,1~(255表示2~19所有8个质数,1表示根节点)

    根据乘法原理,在当前节点确定后,其子树的方案数互不影响,所以当前节点的树的方案数为其子树方案数相乘

    写出状态转移方程:(方程过于复杂,比着代码才写出来,直接看代码吧)

    [dp[i][j]=(dp[i and f[1]][son1]*dp[i and f[1]][son2]*···*f[i and f[1]][sonlast])+(dp[i and f[2]][son1]*···)+···+(dp[i and f[m]][son1]*···) ]

    边界就是叶节点,只要枚举其可行的数字就好,方案不超过20

    因为偷懒实力不允许,这次就用老师的标程作注好了:(为了看着舒服,改了改码风(改得面目全非))

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <cstring>
    using namespace std;
    typedef long long LL;
    vector<int> g[100005];//向量容器,可以用来存树(每个元素(每个向量)存储每个节点的相邻节点(父或子))
    int p[8] = { 2, 3, 5, 7, 11, 13, 17, 19 }, f[25], m, u, v, n;//p存储代号表示的质数,f存储20以内的数的质因数情况
    LL dp[257][100005];//第一维是质因数情况,第二维是节点,所存的数字是指该节点的子树上所有节点的质因数中,这些质因数满足至少有一次没出现(就是当前节点子树至少一对数互质)
    inline LL dfs(int now, int fa, int mask) {//now:当前节点,fa:当前节点的父亲,mask:表示质因数情况,返回值就是(保证mask表示的质数都满足((在now节点为根的子树上所有根叶路径上)都至少有一次没出现))的方案数对1e9+7的取模
        if (dp[mask][now] != -1)//有存储(之前推算到过)避免重复计算
            return dp[mask][now];//直接返回
        LL res = 0;//累计结果
        for (int i = 1; i <= m; i++)//枚举当前节点的数字
            if (g[now].size() == 1 && now != 1)//只有一个连通节点且不是根节点(叶子),因为只要枚举它本身就好,所以答案就一定小于20(不需要取模),满足条件的充要条件就是((now节点的数字)的质因数中)不含有(mask表示的质数)
                res += !(mask & f[i]);//(上一行可知这是个叶节点)与运算后,如果结果不为0,说明(now的数字i的质因数中,有mask表示的质数)也就是说now当前的数字i不满足(以now为根的子树中(所有根叶路径中(mask表示的质数至少有一次没出现)))的条件,i放在这里的now中不合适;反之,结果为0,说明i可以放在now中,这种情况的方案数就会加!(0)也就是1
            else {//不是叶节点,那就考虑其子树
                LL t = 1;//t来记录now为i时子树的方案数
                for (auto to : g[now]) {//auto的用法:作为数组的元素,从该数组第一个到最后一个依次循环(语文水平捉急),这个语句也可以用正常的for循环代替:to相当于g[now][i](传值调用,原数组不动)
                    if (to == fa)
                        continue;//枚举和当前节点连通的节点,如果是父亲,那就跳过,因为是从根到叶递归,所以只看儿子
                    t = t * dfs(to, now, mask & f[i]) % 1000000007;//(根节点确定,子树间互不影响,故遵循乘法原理,把各子树的结果相乘)子树的结果:以子树为根,now为父,mask的情况有些复杂:因为当前的t要求的是以now为根的子树上的根叶路径符合mask的质数(至少一次不出现)条件,并且有f[i]的质数在now出现,所以对子树的要求就是使(f[i]和mask表示的质数)的交集至少有一次不出现,这样,就能保证本层递归结果符合要求,所以用与运算找出交集
                }
                res = (res + t) % 1000000007;//将(now取各种i的值)的各种方案数累加至res里
            }
        return dp[mask][now] = res;//返回并赋值
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n - 1; i++) {//建树
            scanf("%d%d", &u, &v);
            g[u].push_back(v);//新加入一条边
            g[v].push_back(u);//双向边
        }
        for (int i = 1; i <= 20; i++)//枚举1-20的数字
            for (int j = 0; j < 8; j++)//枚举8个质数
                if (!(i % p[j]))//可以整除(含有该质因数)
                    f[i] |= 1 << j;//含有该质因数,对应的二进制位就是1
        memset(dp, -1, sizeof(dp));//打个标记,区分已经遍历到的节点和空白节点
        printf("%lld
    ", dfs(1, 0, 255));//这就是当前节点为1,没有父节点(根),在每一条根到叶的路径中所有质因数都至少一次没出现的方案数
        return 0;
    }
    
  • 相关阅读:
    Java 流(Stream)、文件(File)和IO
    Java集合笔记
    希尔排序
    多线程
    查找算法
    快速排序
    leetcode题解58
    9.回文数
    SonarQube代码质量管理平台安装及使用
    用GitBlit 和 VS GitSourceControlProvider 搭建基于 Http(s) 的 Git 工作平台
  • 原文地址:https://www.cnblogs.com/Wild-Donkey/p/12246445.html
Copyright © 2011-2022 走看看