• 机房模拟测试4:计数类dp+水题+树上计数


    T1:

     原题:P2467 [SDOI2010]地精部落

    分析:

    定义f[i][0/1]为递推到第i位,是奇数项大于两边还是偶数项大于两边。

    但是发现,上面两种情况是等价的,只需要求出一种即可,然后将答案*2。

    假设已经放了i-1个合法的高度,现在将第i个高度放入。

    在i-1个高度中有i个位置是可以插入的,枚举这i个位置。

    假设现在放在第j个位置,那么它将序列划分成了两部分,左边有j-1个,右边有i-j个,合法方案就是f[j-1]*f[i-j]。

    但这还不够,我们发现左右分别放哪些数还不知道,所以还要乘C(i-1,j-1)(在i-1个数中选j-1个放左边)

    注意:模数非质数,不能用费马小定理预处理阶层,要用杨辉三角形递推!!

    #include<bits/stdc++.h>
    using namespace std;
    #define N 4205
    #define ll long long
    #define ri register int
    ll f[N][3],mod;
    int c[N][N];
    int n;
    void init()
    {
        for(ri i=0;i<=n;++i) c[i][i]=1,c[i][0]=1;
        for(ri i=1;i<=n;++i)
         for(ri j=1;j<=n;++j){
             c[i][j]=(c[i-1][j-1]+c[i-1][j]) %mod;
        }
    }
    int main()
    {
        //freopen("rabbit.in","r",stdin);
        //freopen("rabbit.out","w",stdout);
        scanf("%d%lld",&n,&mod);
        init();
        f[0][0]=1; f[0][1]=1; f[1][1]=1; f[1][0]=1; f[2][0]=1; f[2][1]=1;
        for(ri i=3;i<=n;++i){
            for(ri j=1;j<=i;++j){
                if(j&1) f[i][1]=( f[i][1] + f[j-1][1] * f[i-j][1] %mod *1ll*c[i-1][j-1] %mod ) %mod ;
                else    f[i][0]=( f[i][0] + f[j-1][0] * f[i-j][0] %mod *1ll*c[i-1][j-1] %mod ) %mod ;
            }
        }
        printf("%lld
    ",(f[n][0] + f[n][1]) %mod);
    }
    /*
    3 107
    4 107
    4 1000000007
    12 345
    9 233
    233 666
    */
    View Code

    T2:

     分析:

    这道题真的非常的水。。多画图分析一下,会发现因为字符的位置是可以随意变换的,只需要统计同种的个数,讨论n,m的奇偶性即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    int cnt[105],n,m,T;
    char s[205];
    void work1()
    {
        int ans=0;
        for(ri i=1;i<=26;++i) 
        if(cnt[i]%4!=0) { printf("No
    "); return ; }
        printf("Yes
    ");
    }
    void work2(int x)
    {
        int cnt1=0,cnt2=0,cnt4=0;
        for(ri i=1;i<=26;++i)
        if(cnt[i]%4==0) cnt4++;
        else if(cnt[i]%2==0) cnt2++;
        else cnt1++;
        if(cnt1>1 || cnt2>x) printf("No
    ");
        else printf("Yes
    ");
    }
    void work3(int x)
    {
        int cnt2=0,cnt1=0;
        for(ri i=1;i<=26;++i)
        if(cnt[i]%2==0 && cnt[i]%4!=0) cnt2++;
        else if(cnt[i]&1) cnt1++;
        if(cnt2>x || cnt1) printf("No
    ");
        else printf("Yes
    ");
    }
    int main()
    {
        freopen("quilt.in","r",stdin);
        freopen("quilt.out","w",stdout);
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            memset(cnt,0,sizeof(cnt));
            for(ri i=1;i<=n;++i){
                scanf("%s",s);
                for(ri j=0;j<m;++j) cnt[s[j]-'a'+1]++;
            }
            if(n%2==0 && m%2==0) work1();
            else if((n&1) && (m&1)) work2( max( (n-1)/2,(m-1)/2 ) );//
            else work3((n&1) ? m/2 : n/2);
        }
    }
    /*
    5
    3 4
    aabb
    aabb
    aacc
    2 2
    aa
    bb
    5 1
    t
    w
    e
    e
    t
    2 5
    abxba
    abyba
    1 1 
    z
    */
    View Code

    T3:

    一句话题意:根节点可以覆盖d的范围,求两两叶子结点会出发一只蚊子,遇到被覆盖的点就有p/q的概率被杀死,求被杀死的期望蚊子数。

    分析:

    如果是直接统计的期望死亡的蚊子数,会发现对于一个没有被覆盖的点,它子树中期望死亡蚊子数为0,不好转移。

    所以补集转换,转换成求期望存活蚊子数,最后用总数减去即可。

    令k=1-p/q,sum[u]为u子树中期望存活的蚊子数。

    转移分两类:

    1.u没被覆盖:

    ans+=sum[u]*sum[v](点对数相乘即为答案,注意ans要在sum前面统计)

    sum[u]+=sum[v](将v合并入了u)

    2.u被覆盖了:

    ans+=sum[u]*sum[v](这里为什么不再乘一个k呢,因为经过u的生存概率已经在sum[u]中被统计了,所以不在重复统计)

    sum[u]+=sum[v]*k(v子树内的叶子点因为经过了u导致生存概率再乘一个k)

    注意叶子结点被覆盖了应该初始化为k。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define ri register int
    #define N 5000005
    const ll mod=1e9+7;
    ll ans=0,p,q,k,m=0,sum[N];
    int can[N],dep[N],d;
    vector<int> e[N];
    int read()
    {
        int x=0,fl=1; char ch=getchar();
        while(ch<'0' || ch>'9') { if(ch=='-') fl=-1; ch=getchar();  }
        while(ch<='9' && ch>='0') x=x*10+ch-'0',ch=getchar();
        return x*fl;
    }
    ll quick_pow(ll a,ll k)
    {
        ll ans=1;
        while(k){ if(k&1) ans=ans*a %mod; a=a*a %mod; k>>=1; }
        return ans;
    }
    void dfs2(int u,int ff)//只能写一个dfs 否则会TLE 
    {
        int fl=0;
        for(ri i=0;i<e[u].size();++i){
            int v=e[u][i];
            if(v==ff) continue;
            fl=1; dep[v]=dep[u]+1;
            if(dep[v]<=d) can[v]=1;
            dfs2(v,u);
            ans=(ans + sum[u]*sum[v] %mod);
            if(ans>=mod) ans%=mod;
            if(can[u]){
                sum[u]=(sum[u]+sum[v]*k %mod);
                if(sum[u]>=mod) sum[u]%=mod;
            }
            else sum[u]=(sum[u]+sum[v]) %mod; 
            
        }
        if(can[u] && !fl) sum[u]=k;
        else if(!fl) sum[u]=1; 
        m+=(fl==0);
    }
    int main()
    {
        freopen("mosquito.in","r",stdin);
        freopen("mosquito.out","w",stdout);
        int n=read(); int a,b;
        for(ri i=1;i<=n-1;++i) a=read(),b=read(),e[a].push_back(b),e[b].push_back(a);
        d=read(); p=read(); q=read();
        k=(q-p)*quick_pow(q,mod-2) %mod;
        can[1]=1; dfs2(1,0);
        printf("%lld
    ",( (m*(m-1)-2*ans) %mod +mod )%mod);
    }
    /*
    3
    1 2
    1 3
    1 1 2
    
    11
    1 2
    1 3
    2 4
    2 5
    3 6
    4 7
    4 8
    4 9
    5 10
    7 11
    2 1 2
    */
    View Code
  • 相关阅读:
    Linux下C语言执行shell命令
    netstat实现原理
    安装docker
    springboot启动提示连接mysql报错:java.sql.SQLNonTransientConnectionException: CLIENT_PLUGIN_AUTH is required
    linux删除用户报错:userdel: user prize is currently used by process 28021
    centos的6.9版本安装openjdk1.8
    centos的6.9版本安装mysql
    安装mysql报错:Can't find messagefile '/usr/share/mysql/english/errmsg.sys'和/usr/bin/mysqladmin: error while loading shared libraries: libmysqlclient.so.16: cannot open shared object file: No such file or
    安装docker报错:https://download.docker.com/linux/centos/7/i386/stable/repodata/repomd.xml: [Errno 14] PYCURL ERROR 22
    linux配置docker报错:ImportError: No module named yum
  • 原文地址:https://www.cnblogs.com/mowanying/p/11755450.html
Copyright © 2020-2023  润新知