HDU5803 Zhu’s Math Problem (數位DP)

題目鏈接:點擊此處查看

題目大意:
給出ABCD(0ABCD1018) 四個數,求滿足a+c>b+da+db+c 的四元組(a,b,c,d) 的個數,其中0aA,0bB,0cC,0dD

題解:
這題有三種解法。一種是大膽瞎猜推測答案是關於ABCD 的多項式,該多項式最多有24 項,因此設出各項的係數,用小用例解方程組即可。
第二種暴力推公式,也可以得到一個答案的多項式 - -…推不出來..
第三種是數位DP,這個數位DP比較神奇,沒寫過這種數位DP。但是該題也有比較明顯的特點,是給出特定區間,統計滿足條件的數(四元組)的個數。同時對四個數進行數位DP,需要記錄的狀態是每位a+cbda+dbc 的值。因爲0a+b18 ,所以如果某一層的a+bcd2a+bcd2 ,我們就可以認爲後面的位無法再對正負產生影響,那麼只需要{2,1,0,1,2} 五個狀態就好了。每層遞歸內部的複雜度是104 ,加上字符長度20、狀態25和1000組用例..爆炸了。題目的辦法是將數字數字轉化爲2進制再進行DP,從而將每層遞歸的複雜度降爲24 ,這樣的複雜度就可以接受了。
通常在寫數位DP的時候,爲了記憶化數據對每一次查詢都能夠使用,我們會設置一個fp標記到當前位爲止是否是上界的值。在這裏我們需要設置4個上界。4數字個都不爲上界的情況實際上是比較少的。因此如果只記憶化非上界的值,會有大量的重複運算。在這裏只能放棄記憶化對所有查詢的貢獻,而要多開四維狀態來記錄每個數是否處於上界,這四維狀態可以壓位處理。因爲DP數組只記錄特定的查詢的DP值,所以每次查詢的時候需要對DP數組重新初始化。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <bitset>

using namespace std;
typedef long long ll;
const int mod = 1000000007;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const ll INF = 100000000000000000ll;
const int MAXN = 100010;
const int MAXM = 300030;

int d1[67],d2[67],d3[67],d4[67];
int dp[67][4][4][16];

int dfs(int len,int s1,int s2,int mask){
    if(!len){
        if(s1>0&&s2>=0) return 1;
        return 0;
    }
    if(dp[len][s1+1][s2+1][mask] != -1) return dp[len][s1+1][s2+1][mask];
    int a,b,c,d;
    a = (mask&8)?d1[len]:1; b = (mask&4)?d2[len]:1; c = (mask&2)?d3[len]:1; d = (mask&1)?d4[len]:1;
    int ret = 0;
    for(int i = 0;i <= a;i++){
        for(int j = 0;j <= b;j++){
            for(int k = 0;k <= c;k++){
                for(int l = 0;l <= d;l++){
                    int t1 = s1*2,t2 = s2*2,t = 0;
                    t1 += i+k-j-l;
                    t2 += i+l-j-k;
                    if(t1<-1||t2<-1){
                        continue;
                    }
                    if(t1>1) t1 = 2;
                    if(t2>1) t2 = 2;
                    if(i==a) t|=8; if(j==b) t|=4; if(k==c) t|=2; if(l==d) t|=1;
                    ret += dfs(len-1,t1,t2,mask&t); ret %= mod;
                }
            }
        }
    }
    return dp[len][s1+1][s2+1][mask] = ret;
}

int f(ll a,ll b,ll c,ll d){
    int len = 0;
    while(a||b||c||d){
        d1[++len] = a&1; d2[len] = b&1; d3[len] = c&1; d4[len] = d&1;
        a>>=1; b>>=1; c>>=1; d>>=1;
    }
    return dfs(len,0,0,15);
}

int main()
{
    int T;
    //freopen("1011.in","r",stdin);
    //freopen("out","w",stdout);
    cin>>T;
    ll a,b,c,d;
    while(T--){
        memset(dp,-1,sizeof(dp));
        scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&d);
        printf("%d\n",f(a,b,c,d));
        //printf("cnt = %I64d nn = %I64d\n",cnt,nn);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章