USACO 2014 US Open Odometer /// 數位DP

題目大意:c++

給定區間 l rspa

求得區間中有多少個數 數的各個數位裏出現最屢次的數>=數的長度的一半 如2233 3334code

 

枚舉k在數中出現次數在一半以上 那麼求出的全部方案數中應該減去 兩個數各佔一半的狀況blog

 

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define INF 0x3f3f3f3f
#define mem(i,j) memset(i,j,sizeof(i))
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define gcd(i,j) __gcd(i,j);
const int N=55;
const int mod=1e9+7;
const double eps=1e-8;

LL dp[N][N][2][2];
// dp[i][j][b1][b2]
// i爲數的第i位 j做爲枚舉的k出現次數的相對計數器
// b1=1當前位小於上界 =0則是等於上界
// b2=1到當前位以前全是前導0 =0則不是前導0
LL DP(char t[],int n,int k1,int k2) {
    mem(dp,0);
    dp[0][25][0][1]=1; // 初始設25 防止負數
    inc(i,0,n-1) inc(j,0,N-1)
        inc(b1,0,1) inc(b2,0,1) {
            LL cur=dp[i][j][b1][b2];
            inc(nxt,0,9) { // 枚舉下一位
                if(k2!=-1) { // 說明枚舉的是2233這種被兩個數各佔一半的狀況
                    if(b2==0 || nxt!=0) // 下一位不是前導0
                        if(nxt!=k1 && nxt!=k2) continue; // 但又不是這兩種數
                }
                if(b1==0 && nxt>t[i]-'0') continue;
                // 當前位已是上界 那麼下一位不能超過上界
                bool nb2= b2&(nxt==0); // 當前位是0b2爲1 下一位爲0nxt==0爲1 則nb2爲1
                int nj=j;
                if(nb2==0) { // 下一位不是前導0
                    if(nxt==k1) nj--;
                    else nj++;
                } // 是k1就+ 不是就- 最後j=25說明k1恰好一半 若是j<25說明k1超過半數
                bool nb1= b1|(nxt<t[i]-'0'); // 當前位以前均爲上界b1=0 下一位爲上界nxt<t[i]-'0'=0 則nb1爲0
                dp[i+1][nj][nb1][nb2]+=cur;
            }
        }
    LL res=dp[n][25][1][0]+dp[n][25][0][0];
    if(k2==-1) inc(j,0,24)
        res+=dp[n][j][1][0]+dp[n][j][0][0];
    return res;
}

LL ANS(char t[],int n) {
    LL res=0;
    inc(k,0,9) res+=DP(t,n,k,-1); // k出現次數超過一半 如2223
    inc(k1,0,9) inc(k2,k1+1,9) // k1 k2各佔一半的 如2323
        res-=DP(t,n,k1,k2); 
    return res;
}

int main()
{
    LL ta,tb;
    while(~scanf("%lld%lld",&ta,&tb)) {
        ta--;
        char a[20],b[20];
        int lena=0,lenb=0;
        while(ta) a[lena++]=ta%10+'0', ta/=10;
        while(tb) b[lenb++]=tb%10+'0', tb/=10;
        reverse(a,a+lena); reverse(b,b+lenb);
        printf("%lld\n",ANS(b,lenb)-ANS(a,lena));
    }

    return 0;
}
相關文章
相關標籤/搜索