2016-ChinaFinal-H
题意:
给出n,m,k,在n*m的网格内填[1,k]的整数,定义一个格子是great的,如果满足填在这个格子中的数是本行和本列中严格的最大值。定义Ag为网格中恰好有g个great格子的填法数,求Σ(g+1)Ag。
参考博客
题解:
好像可以用组合数学的容斥原理计算,不过上诉博客的做法我觉得思维性很强,想清楚后很简单,就是一个快速幂。
做法是观察整体,把问题转化成每个位置是great格子对最终答案的贡献和,这样就绕开了Ag的计算。(不知道怎么想到的)
如何绕开Ag的计算? 其实就是Σ(g+1)Ag = Σg*Ag+ΣAg。有点思维性的来了,这里ΣAg其实就等于整体填数法(K^(M*N))。仔细想象很容易理解,就是每种填数法对应于ΣAg中的一种情况。
接下来的问题就是Σg*Ag的计算了,这个思维性更强。用贡献和的思想转化,因为有g个great格的填法数乘了个g相当于摊到了这g个位置上,也就是说每个great格独立了,也即每一个格是great格对Σg*Ag贡献=使这个格是great的所有填法总数:Contrib=Σ(i=1到i=K-1)i^(N-1+M-1)×K^[(N-1)×(M-1)] 。(什么鬼其实我也不太懂,懂的同学麻烦评论让大家学习学习)
我们这样想想,假设3个great格的Ag=2,那么每个Ag里面的三个great格的对应的填法加起来后刚好就是3*2,类推每个格子的great填法加起来就是Σg*Ag。
这里注意一点:以上公式没有考虑n=m=1的情况,因为这时填任意数也算是great格。
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
ll n,m,k;
ll mod = 1e9+7;
ll pow_mod(ll a,ll b)
{
ll ans = 1;
while(b)
{
if(b&1) ans = (ans*a)%mod;
a = (a*a)%mod;
b >>= 1;
}
return ans;
}
int main()
{
int t;
ll ans;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
ans = 0;
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=1;i<k;i++)
{
ans = (ans+pow_mod(i,n+m-2))%mod;
}
ans = ans*pow_mod(k,(n-1)*(m-1))%mod;
ans = ans*n%mod*m%mod;
ans = (ans+pow_mod(k,m*n))%mod;
if(n==1&&m==1) ans=(ans+1)%mod;
printf("Case #%d: %lld\n",ca,ans);
}
return 0;
}
这里不知道什么鬼,我把n,m,k写成int时是错的,我觉得快速幂的时候应该不会爆int啊。