[AGC039F]Min Product Sum
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[AGC039F]Min Product Sum相关的知识,希望对你有一定的参考价值。
Min Product Sum
题解
首先,它是要求每个位置上当前行与当前列所有数的最小值的乘积。
我们不妨把它给转化一下,我们现在要对一个矩阵
A
A
A和矩阵
B
B
B进行赋值,要求矩阵
B
B
B上的数不大于
A
A
A上相同行与相同列上的数。
我们现在得设计一个
d
p
dp
dp状态来对它进行转移,先观察一下我们填数有什么性质。
显然,当
A
A
A中的某行或某列填了数后,
B
B
B中该行或该列就不能再填数了,同样,
A
A
A也只能在某行或某列是满的情况下在这行或者列上填数。
我们的
d
p
dp
dp状态最好关联到两个矩阵与每行每列,不妨就来定义
d
p
i
,
j
,
k
dp_i,j,k
dpi,j,k表示现在枚举到数
i
i
i,已经
B
B
B中已经有
j
j
j行填满了,
A
A
A中有
k
k
k列填了数。
我们转移这个状态就可以去枚举在
i
i
i的时候,新填满了几行,开始填了几列。
我们先枚举
B
B
B中填的行,如果现在我们新填了一行,我们可以确定两点:
- 该行上, A A A中还没有填数的列在网格 B B B上可以自由选择 [ 1 , i ] [1,i] [1,i]填充。
- 该行上, A A A中已经填了数的列在网格 A A A上可以自由选择 [ i , K ] [i,K] [i,K]填充。
同时,我们还必须保证这行有数填到
i
i
i,因为它在这行才填满。
那么我们选择这行产生的贡献就是
(
i
m
−
k
−
(
i
−
1
)
m
−
k
)
(
K
−
i
+
1
)
k
(i^m-k-(i-1)^m-k)(K-i+1)^k
(im−k−(i−1)m−k)(K−i+1)k,剩下的
A
A
A中已经填了数的列在
B
B
B上的位置时要求比
A
A
A上这行的最小值小的,我们应该当那一列确定时就把它填了,而不是等到现在。
之后我们枚举
A
A
A中现在开始填的行的时候,也是用类似的方法:
- 该列上, B B B中已经填满数的行在网格 A A A上可以自由选择 [ i , K ] [i,K] [i,K]填充。
- 该列上, B B B中还没填满数的行在网格 B B B上可以自由选择 [ 1 , i ] [1,i] [1,i]填充。
同样,我们也得保证这列有数填到
i
i
i。至于
B
B
B中还没有填满数的行,在
A
A
A中的这列上的数我们现在不能填,否则就不核合法了,我们只能在之后这行填满后再填这个位置。
容易算出我们选择这列时产生的贡献为
(
(
K
−
i
+
1
)
j
−
(
K
−
i
)
j
)
i
n
−
j
((K-i+1)^j-(K-i)^j)i^n-j
((K−i+1)j−(K−i)j)in−j。
另外,新添行与列的时候我们是需要乘上个组合数来算我们在所有行列中找的哪些来填的。
总时间复杂度 O ( k n m ( n + m ) ) O\\left(knm(n+m)\\right) O(knm(n+m))。
源码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
#define MAXN 200005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const LL INF=0x3f3f3f3f3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
const int n1=500;
const int M=MAXN/n1+5,N=n1+5;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,K,P,dp[2][105][105],C[105][105];
int main()
read(n);read(m);read(K);read(P);
for(int i=0;i<=max(n,m);i++)
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=add(C[i-1][j-1],C[i-1][j],P);
int now=0,las=1;dp[now][0][0]=1;
for(int i=1;i<=K;i++)
swap(now,las);
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++)if(dp[las][j][k])
const int tp=1ll*(qkpow(i,m-k,P)+P-qkpow(i-1,m-k,P))*qkpow(K-i+1,k,P)%P;
for(int l=0,nw=1;l<=n-j;l++,nw=1ll*tp*nw%P)
Add(dp[now][j+l][k],1ll*nw*dp[las][j][k]%P*C[n-j][l]%P,P);
dp[las][j][k]=0;
swap(now,las);
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++)if(dp[las][j][k])
const int tp=1ll*(qkpow(K-i+1,j,P)+P-qkpow(K-i,j,P))*qkpow(i,n-j,P)%P;
for(int l=0,nw=1;l<=m-k;l++,nw=1ll*tp*nw%P)
Add(dp[now][j][k+l],1ll*nw*dp[las][j][k]%P*C[m-k][l]%P,P);
dp[las][j][k]=0;
printf("%d\\n",dp[now][n][m]);
return 0;
谢谢!!!
以上是关于[AGC039F]Min Product Sum的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder AGC039F Min Product Sum (容斥原理组合计数DP)
AtCoder Grand Contest 039 F: Min Product Sum
AtCoder Grand Contest 039 F: Min Product Sum