zoukankan      html  css  js  c++  java
  • 题解--luogu--CSP2019.S.Day2.T4--Emiya 家今天的饭

    今年CSP我不想说了,想AFO

    我看了很久luogu题解,才略微懂了点

    P5664 Emiya 家今天的饭【民间数据】

    首先这是一道dp考场上没看出来?



    思路分析

    1.Emiya的条件很容易满足。
    
    2.Rin的条件也是比较死的,同一烹饪方法只有一种菜(也就是输入中的每一行只有一个菜)
    
    3.Yazid的要求很特殊,先不考虑。

    可以看出,维护每列已选的节点复杂度太大,不太可行;因此很容易想到,先不考虑每列不超过一半的这个限制,求出总方案数,然后再减去考虑这个限制后不合法的方案数。现在问题就变成,求任意列选的节点超过所有选的节点的一半的方案数之和。

    总数:每行的可能数相乘。见下代码。

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            a[i][j]=read();
            sum[i]=(a[i][j]+sum[i])%mod; //每种烹饪一共能做多少菜
        }
        ans=(ans*(sum[i]+1))%mod; //sum[i]+1是因为可以不用某种烹饪
    }
    ans--; //ans-1是因为不能一样菜都没有

    接下来咋处理?

    设f[i][j][k]表示前i行选j个节点,当前枚举到的列选k个节点的方案数。对于每个列,复杂度为O($n^3$),总的复杂度为O($mn^3$),可以得到84分的高分。

    要拿满分怎么办?

    考虑将某两个状态合并!!!“观察状态,实际上我们想知道的只是j,k的大小关系(即k>
    ⌊j/2
     ),对于具体的值并不关心,考虑将它们合并到一维。” 
    考虑我们需要的限制条件j>⌊k/2 ,变形一下可以得到2*k+n-j>n

    观察这个式子,可以发现,nj就是这n行里没有选的行数。2*k就只需要存两次就行了

    然后一个奇妙的想法就出来了:

    对于每个节点,选它时,当做该列选了两次。

    而对于某一行不选时,当做所有列选了一次。

    最终要找的就是当前列被选超过n次的方案。这样就成功地优化掉了第二维。

    f[i][k]=(f[i][k]+f[i-1][k]*(sum[i]-a[i][j]))%mod;//若不选此列,所以总次数是每行除开a[i][j]外的任何数之积。
    f[i][k+1]=(f[i][k+1]+f[i-1][k])%mod;//若不选此行,总次数是i-1行的方案,和这一行没任何关系,直接改变k+1的值
    f[i][k+2]=(f[i][k+2]+f[i-1][k]*a[i][j])%mod; //若要选节点,同上面的两次分i-1行的值乘上该节点值,也就是k+2的值了

    注意:开long long

    代码如下:

    #include<cstdio> 
    #include<string>
    #define ll long long 
    using namespace std;
    
    const int maxn=105,maxm=2005,mod=998244353;
    int n,m;
    ll a[maxn][maxm];
    ll sum[maxn];
    ll ans=1;
    ll f[maxn][maxm];
    
    int read()
    {
        int x=0,f=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
        while(isdigit(c)){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    void readdata()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                a[i][j]=read();
                sum[i]=(a[i][j]+sum[i])%mod;
            }
            ans=(ans*(sum[i]+1))%mod;
        }
        ans--;
        for(int j=1;j<=m;j++)
        {
            memset(f,0,sizeof(f));
            f[0][0]=1;
            for(int i=1;i<=n;i++)
            {
                for(int k=0;k<=(i-1)<<1;k++) //第i行可能涉及到(i-1)*2,"<<1"相当于“*2”
                {
                    f[i][k]=(f[i][k]+f[i-1][k]*(sum[i]-a[i][j]))%mod;
                    f[i][k+1]=(f[i][k+1]+f[i-1][k])%mod;
                    f[i][k+2]=(f[i][k+2]+f[i-1][k]*a[i][j])%mod;
                }
                
            }
            for(int k=n+1;k<=2*n;k++)
            {
                ans=(ans-f[n][k]+mod)%mod; //每次会重复
            }
        }
        printf("%lld",ans%mod);
    }
    
    int main()
    {
        readdata();
        return 0;
    }

    AFO^……

  • 相关阅读:
    BZOJ2705[SDOi2012]Longge的问题
    Ubuntu 18.04 打不开1.1.0版本网易云音乐的解决方法汇总
    BZOJ3295[CQOI2011]动态逆序对(CDQ分治)
    hdu-4638-Group(树状数组)
    hdu-3333-Turing Tree(树状数组)
    UVA-11983-Weird Advertisement(线段树+扫描线)[求矩形覆盖K次以上的面积]
    ZOJ-3597-Hit the Target!(线段树+扫描线)
    POJ-1177-Picture(线段树+扫描线+离散化)[矩形周长并]
    POJ-1151-Atlantis(线段树+扫描线+离散化)[矩形面积并]
    LightOJ 1135(线段树)
  • 原文地址:https://www.cnblogs.com/mzyczly/p/11914291.html
Copyright © 2011-2022 走看看