NOI2018湖北省隊集訓Day3 T1 貪婪人
阿新 • • 發佈:2019-01-03
題面:
得分情況:
爆零。。。
正解:
我們用dp[n][m][w]表示在n*m的網格中,貪心路徑權值為w的方案數,轉移時列舉向右和向下,時間複雜度,能過70%的資料。
我們首先可以通過維護字首和去掉一個s,將複雜度降為,然後你還是隻能過70%的點。
我們又發現,每次在不是圖的右邊界的位置向下走至少會對答案作出1的貢獻(如果下面格子的權值為0必定會向右走),所以當走了s步之後必然會來到圖的最右邊,及經過點(s+1,w),所以我們只需要對s*w的格子做dp,其他的直接轉移即可,總複雜度為,可以通過所有資料。
程式碼:
#include <bits/stdc++.h>
using namespace std;
const int mod=10007,maxn=3000,maxs=110;
int f1[maxn*2][maxs],f2[maxn*2][maxs],f3[maxn*2][maxs];
int fact[maxn<<3],invfact[maxn<<3];
int modpow(int x,int y)
{
int nowans=1;
while(y>0)
{
if(y%2==1) nowans=(nowans*x)%mod;
x=(x*x)%mod;
y/=2 ;
}
return nowans;
}
int calcu(int n,int k)
{
int p=fact[n];
p=(p*invfact[k])%mod;
p=(p*invfact[n-k])%mod;
return p;
}
int sum(int horz,int vert,int forced,int s)
{
int nowans = 0;
for (int a=0; a<=s; a++) {
for (int b=0; a+b<=s; b++) {
int c = s - b - a;
int p = f1[horz][a];
p *= f2[vert][b];
p %= mod;
p *= f3[forced][c];
p%=mod;
nowans += p;
nowans%=mod;
}
}
return nowans;
}
int h,w,s,ans;
int main()
{
scanf("%d%d%d",&h,&w,&s);
f1[0][0]=f2[0][0]=f3[0][0]=1;
for(int i=1;i<=h+w+1;i++)
for(int num=0;num<=s;num++)
{
f1[i][num]=f2[i][num]=f3[i][num]=0;
for(int x=0;x<=num;x++)
{
f1[i][num]+=f1[i-1][num-x]*(x+1);
f2[i][num]+=f2[i-1][num-x]*x;
f3[i][num]+=f3[i-1][num-x];
}
f1[i][num]%=mod;
f2[i][num]%=mod;
f3[i][num]%=mod;
}
if(min(w,h)==1) { printf("%d\n",f3[w*h-1][s]);return 0; }
fact[0]=1;
invfact[0]=1;
for(int i=1;i<=w+h+1;i++)
{
fact[i]=(fact[i-1]*i)%mod;
invfact[i]=modpow(fact[i],mod-2);
}
for(int i=0;i<=w-2;i++)
{
int horz=i;
int vert=h-1;
int forced=w-i-1;
int free=w*h-1-forced-2*horz-2*vert;
int q=(calcu(horz+vert-1,horz)*modpow(s+1,free))%mod;
q=(q*sum(horz,vert,forced,s))%mod;
ans=(ans+q)%mod;
}
for(int j=0;j<=h-2;j++)
{
int horz=w-1;
int vert=j;
int forced=h-j-1;
int free=w*h-1-forced-2*horz-2*vert;
int q=(calcu(horz+vert-1,vert)*modpow(s+1,free))%mod;
q=(q*sum(horz,vert,forced,s))%mod;
ans=(ans+q)%mod;
}
printf("%d\n",ans);
return 0;
}