zoukankan      html  css  js  c++  java
  • agc039f

    https://atcoder.jp/contests/agc039/tasks/agc039_f

    由于难以直接计数,我们不妨虑容斥。假设我们枚举了每一行、每一列的最小值分别是什么,那么可以算出这种情况的贡献为prod_{i=1}^{n}prod_{j=1}^{m}{max{a[i],b[j]}}。而计算方案数可以枚举哪些列、哪些行上是强制不能满足条件的,也就是a[i]或b[i]加1。这部分可以看代码1。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef long long int ll;
      4 const int maxn=105;
      5 ll n,m,K,mod,f[maxn][maxn][maxn],C[maxn][maxn];
      6 ll fac[maxn],inv[maxn];
      7 inline ll qpow(ll x,ll y)
      8 {
      9     ll ans=1,base=x;
     10     while(y)
     11     {
     12         if(y&1)
     13             ans=ans*base%mod;
     14         base=base*base%mod;
     15         y>>=1;
     16     }
     17     return ans;
     18 }
     19 inline void init()
     20 {
     21     fac[0]=1;
     22     for(int i=1;i<=100;++i)
     23         fac[i]=fac[i-1]*i%mod;
     24     for(int i=0;i<=100;++i)
     25         inv[i]=qpow(fac[i],mod-2);
     26     C[0][0]=1;
     27     for(int i=1;i<=100;++i)
     28     {
     29         C[i][0]=1;
     30         for(int j=1;j<=i;++j)
     31             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
     32     }
     33 }
     34 int a[55],b[55];
     35 ll ans;
     36 inline ll get()
     37 {
     38     ll s=0;
     39     for(int s1=0;s1<(1<<n);++s1)
     40         for(int s2=0;s2<(1<<m);++s2)
     41         {
     42             ll g=1;
     43             for(int i=1;i<=n;++i)
     44                 for(int j=1;j<=m;++j)
     45                 {
     46                     bool fx=(s1&(1<<(i-1)))>0;
     47                     bool fy=(s2&(1<<(j-1)))>0;
     48                     int x=a[i],y=b[j];
     49                     x+=fx,y+=fy;
     50                     g=g*(K+1-max(x,y))%mod;
     51                 }
     52             for(int i=0;i<n;++i)
     53                 if(s1&(1<<i))
     54                     g=-g;
     55             for(int i=0;i<m;++i)
     56                 if(s2&(1<<i))
     57                     g=-g;
     58             s=(s+g)%mod;
     59         }
     60     return s;
     61 }
     62 void dfs(int s,int d)
     63 {
     64     if(s==n+m+1)
     65     {
     66         ll g=1;
     67         for(int i=1;i<=n;++i)
     68             for(int j=1;j<=m;++j)
     69                 g=g*min(a[i],b[j])%mod;
     70         ans=(ans+g*get())%mod;
     71         return;
     72     }
     73     if(s<=n)
     74         for(int i=1;i<=K;++i)
     75         {
     76             a[s]=i;
     77             if(i&1)
     78                 dfs(s+1,d);
     79             else
     80                 dfs(s+1,-d);
     81         }
     82     else
     83         for(int i=1;i<=K;++i)
     84         {
     85             b[s-n]=i;
     86             if(i&1)
     87                 dfs(s+1,d);
     88             else
     89                 dfs(s+1,-d);
     90         }
     91 }
     92 inline void solve()
     93 {
     94     dfs(1,1);
     95     cout<<ans<<endl;
     96 }
     97 int main()
     98 {
     99     ios::sync_with_stdio(false);
    100     cin>>n>>m>>K>>mod;
    101     init();
    102     solve();
    103     return 0;
    104 }
    1

    注意到我们只关心相对的大小关系,并不关心它们之间具体的位置,并且在暴力计算中所有的贡献都可以看成是“独立”的。我们设f[i][j][k]为填了大小为i*j的方格,并且最大的数已经填到k(当然,可以不填),我们枚举不被容斥的行数i1、被容斥的行数i2、不被容斥的列数j1、被容斥的列数j2。容斥系数显然为(-1)^(i2+j2),其它的转移系数可以通过画图的方法来得知(或者你可以看下面的图)。这部分可以看代码2。复杂度是七次方的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long int ll;
     4 const int maxn=105;
     5 ll n,m,K,mod,f[maxn][maxn][maxn],C[maxn][maxn];
     6 ll fac[maxn],inv[maxn];
     7 inline ll qpow(ll x,ll y)
     8 {
     9     ll ans=1,base=x;
    10     assert(y>=0);
    11     while(y)
    12     {
    13         if(y&1)
    14             ans=ans*base%mod;
    15         base=base*base%mod;
    16         y>>=1;
    17     }
    18     return ans;
    19 }
    20 inline void init()
    21 {
    22     fac[0]=1;
    23     for(int i=1;i<=100;++i)
    24         fac[i]=fac[i-1]*i%mod;
    25     for(int i=0;i<=100;++i)
    26         inv[i]=qpow(fac[i],mod-2);
    27     C[0][0]=1;
    28     for(int i=1;i<=100;++i)
    29     {
    30         C[i][0]=1;
    31         for(int j=1;j<=i;++j)
    32             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    33     }
    34 }
    35 inline void add(ll&x,ll y)
    36 {
    37     x=(x+y)%mod;
    38 }
    39 inline void solve()
    40 {
    41     f[0][0][0]=1;
    42     for(int k=0;k<K;++k)
    43         for(int i=0;i<=n;++i)
    44             for(int j=0;j<=m;++j)
    45                 if(f[i][j][k])
    46                 for(int i1=0;i1+i<=n;++i1)
    47                     for(int j1=0;j1+j<=m;++j1)
    48                         for(int i2=0;i1+i+i2<=n;++i2)
    49                             for(int j2=0;j1+j+j2<=m;++j2)
    50                             {
    51                                 ll s1=inv[i1]%mod*inv[i2]%mod*inv[j1]%mod*inv[j2]%mod;
    52                                 ll s2=qpow(K+1-(k+2),(i+i1+i2)*j2+(j+j1+j2)*i2-i2*j2);
    53                                 ll s3=qpow(K+1-(k+1),(i+i1)*j1+(j+j1)*i1-i1*j1);
    54                                 ll s4=qpow(k+1,(n-i)*(j1+j2)+(m-j)*(i1+i2)-(i1+i2)*(j1+j2));
    55                                 if((i2+j2)&1)
    56                                     add(f[i+i1+i2][j+j1+j2][k+1],-f[i][j][k]*s1%mod*s2%mod*s3%mod*s4%mod);
    57                                 else
    58                                     add(f[i+i1+i2][j+j1+j2][k+1],f[i][j][k]*s1%mod*s2%mod*s3%mod*s4%mod);
    59                             }
    60     ll ans=f[n][m][K]*fac[n]%mod*fac[m]%mod;
    61     ans=(ans%mod+mod)%mod;
    62     cout<<ans<<endl;
    63 }
    64 int main()
    65 {
    66     ios::sync_with_stdio(false);
    67     cin>>n>>m>>K>>mod;
    68     init();
    69     solve();
    70     return 0;
    71 }
    2

    实际上,我们发现转移系数只和“面积”有关,因此我们对于枚举的四个系数可以分开来考虑。

    具体来讲,转移时先枚举区域A,转移系数为(K+1-(k+1))^A的面积;接着枚举区域B,转移系数为(K+1-(k+1))^B的面积;接着枚举区域C,转移系数为(K+1-(k+2))^C的面积*(-1)^C的面积;接着枚举区域D,转移系数为(K+1-(k+2))^D的面积*(-1)^C的面积。这个是方案数的系数,而价值的系数就是(k+1)^(左坐标(i,j)左上角且不在左边(i+i1+i2,j+j1+j2)左上的面积)。预处理所有需要用到的i^k,复杂度四次方。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long int ll;
     4 const int maxn=105;
     5 ll n,m,K,mod,f[maxn][maxn][maxn],C[maxn][maxn],pre[maxn][maxn*maxn];
     6 ll tmp1[maxn][maxn],tmp2[maxn][maxn];
     7 ll fac[maxn],inv[maxn];
     8 inline ll qpow(ll x,ll y)
     9 {
    10     ll ans=1,base=x;
    11     assert(y>=0);
    12     while(y)
    13     {
    14         if(y&1)
    15             ans=ans*base%mod;
    16         base=base*base%mod;
    17         y>>=1;
    18     }
    19     return ans;
    20 }
    21 inline void init()
    22 {
    23     fac[0]=1;
    24     for(int i=1;i<=100;++i)
    25         fac[i]=fac[i-1]*i%mod;
    26     for(int i=0;i<=100;++i)
    27         inv[i]=qpow(fac[i],mod-2);
    28     C[0][0]=1;
    29     for(int i=1;i<=100;++i)
    30     {
    31         C[i][0]=1;
    32         for(int j=1;j<=i;++j)
    33             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    34     }
    35     for(int k=0;k<=K+2;++k)
    36     {
    37         pre[k][0]=1;
    38         for(int i=1;i<=n*m;++i)
    39             pre[k][i]=pre[k][i-1]*k%mod;
    40     }
    41 }
    42 inline void add(ll&x,ll y)
    43 {
    44     x=(x+y)%mod;
    45 }
    46 inline void solve()
    47 {
    48     f[0][0][0]=1;
    49     for(int k=0;k<K;++k)
    50     {
    51         memset(tmp1,0,sizeof(tmp1));
    52         for(int i=0;i<=n;++i)
    53             for(int j=0;j<=m;++j)
    54                 for(int i1=0;i1+i<=n;++i1)
    55                     add(tmp1[i+i1][j],f[i][j][k]*inv[i1]%mod*pre[K+1-(k+1)][i1*j]%mod*pre[k+1][(m-j)*i1]);
    56         memset(tmp2,0,sizeof(tmp2));
    57         for(int i=0;i<=n;++i)
    58             for(int j=0;j<=m;++j)
    59                 for(int j1=0;j1+j<=m;++j1)
    60                     add(tmp2[i][j+j1],tmp1[i][j]*inv[j1]%mod*pre[K+1-(k+1)][j1*i]%mod*pre[k+1][(n-i)*j1]);
    61         memset(tmp1,0,sizeof(tmp1));
    62         for(int i=0;i<=n;++i)
    63             for(int j=0;j<=m;++j)
    64                 for(int i2=0;i2+i<=n;++i2)
    65                     if(i2&1)
    66                         add(tmp1[i+i2][j],-tmp2[i][j]*inv[i2]%mod*pre[K+1-(k+2)][i2*j]%mod*pre[k+1][(m-j)*i2]);
    67                     else
    68                         add(tmp1[i+i2][j],tmp2[i][j]*inv[i2]%mod*pre[K+1-(k+2)][i2*j]%mod*pre[k+1][(m-j)*i2]);
    69         memset(tmp2,0,sizeof(tmp2));
    70         for(int i=0;i<=n;++i)
    71             for(int j=0;j<=m;++j)
    72                 for(int j2=0;j2+j<=m;++j2)
    73                     if(j2&1)
    74                         add(tmp2[i][j+j2],-tmp1[i][j]*inv[j2]%mod*pre[K+1-(k+2)][j2*i]%mod*pre[k+1][(n-i)*j2]);
    75                     else
    76                         add(tmp2[i][j+j2],tmp1[i][j]*inv[j2]%mod*pre[K+1-(k+2)][j2*i]%mod*pre[k+1][(n-i)*j2]);
    77         for(int i=0;i<=n;++i)
    78             for(int j=0;j<=m;++j)
    79                 f[i][j][k+1]=tmp2[i][j];
    80     }
    81     ll ans=f[n][m][K]*fac[n]%mod*fac[m]%mod;
    82     ans=(ans%mod+mod)%mod;
    83     cout<<ans<<endl;
    84 }
    85 int main()
    86 {
    87     ios::sync_with_stdio(false);
    88     cin>>n>>m>>K>>mod;
    89     init();
    90     solve();
    91     return 0;
    92 }
    3
  • 相关阅读:
    了解下Mysql的间隙锁及产生的原因
    Mysql在InnoDB引擎下索引失效行级锁变表锁案例
    Mysql优化_慢查询开启说明及Mysql慢查询分析工具mysqldumpslow用法讲解
    Mysql优化_ORDER BY和GROUP BY 的优化讲解(单路排序和双路排序)
    Mysql优化原则_小表驱动大表IN和EXISTS的合理利用
    Mysql性能调优工具Explain结合语句讲解
    隐藏Apche、Nginx、PHP的版本号提高网站安全性
    Thinkphp在Lnmp环境下部署项目先后报错问题解决:_STORAGE_WRITE_ERROR_:./Application/Runtime/Cache/Home/...Access denied.
    html中返回上一页
    ecshop网站建设手机版wap版出现lib.debug.php on line 303
  • 原文地址:https://www.cnblogs.com/GreenDuck/p/13708535.html
Copyright © 2011-2022 走看看