1. 程式人生 > >NOI2018湖北省隊集訓Day3 T1 貪婪人

NOI2018湖北省隊集訓Day3 T1 貪婪人

題面:
這裡寫圖片描述

得分情況:
爆零。。。

正解:
我們用dp[n][m][w]表示在n*m的網格中,貪心路徑權值為w的方案數,轉移時列舉向右和向下,時間複雜度O(hws2),能過70%的資料。
我們首先可以通過維護字首和去掉一個s,將複雜度降為O(hws),然後你還是隻能過70%的點。
我們又發現,每次在不是圖的右邊界的位置向下走至少會對答案作出1的貢獻(如果下面格子的權值為0必定會向右走),所以當走了s步之後必然會來到圖的最右邊,及經過點(s+1,w),所以我們只需要對s*w的格子做dp,其他的直接轉移即可,總複雜度為O(ws

2),可以通過所有資料。

程式碼:

#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; }