ICPC2020 濟南站 L 題 (數位dp)
阿新 • • 發佈:2020-12-29
題目連結:
發現 \(m\) 很小,所以只需要先數位 \(dp\),然後暴力列舉後 \(8\) 位的情況即可,因為涉及到進位,所以還需要維護第八位之前有幾個連續的 \(1\),
設 \(dp[0/1][i][0/1][0/1]\) 表示,當前到第 \(i\) 位,卡不卡上界,從第 \(i\) 位向前有多少個連續的 \(1\)(奇偶), 有多少個 \(1\) (奇偶)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 110; int T, m, lev; ll L; int a[maxn], c[maxn], d[maxn], cnt = 0; int su[600]; ll A1[2][65],A2[2][65]; ll dp[2][65][2][2]; ll dfs(int pos, int sum, int in, int lim){ if(dp[lim][pos][sum & 1][in & 1] != -1) return dp[lim][pos][sum & 1][in & 1]; if(pos >= cnt + 1 - 8){ ll res = 0; if(lim) res += A1[sum & 1][in & 1]; else res += A2[sum & 1][in & 1]; dp[lim][pos][sum & 1][in & 1] = res; return res; } ll res = 0; int li = (lim) ? (c[pos]) : 1; for(int i = 0 ; i <= li ; ++i){ if(i == 0) res += dfs(pos + 1, sum + i, 0, lim & (i == li)); else res += dfs(pos + 1, sum + i, in + i, lim & (i == li)); } dp[lim][pos][sum & 1][in & 1] = res; return res; } ll calc(ll x){ cnt = 0; ll tmp = x; while(tmp){ c[++cnt] = tmp % 2ll; tmp /= 2ll; } for(int i = 1 ; i <= cnt / 2 ; ++i) swap(c[i], c[cnt - i + 1]); for(int sum=0;sum<=1;++sum){ for(int in = 0;in <= 1;++in ){ for(int i = 0 ; i <= lev ; ++i){ int tmp=1; for(int j = 0 ; j < m ; ++j){ if(i+j>=256){ tmp &= (((sum + su[i+j] - in)%2+2)%2== a[j]); }else{ tmp &= (((sum + su[i+j]) % 2+2)%2 == a[j]); } } A1[sum][in]+=tmp; } for(int i = 0 ; i < 256 ; ++i){ int tmp=1; for(int j = 0 ; j < m ; ++j){ if(i+j>=256){ tmp &= (((sum + su[i+j] - in)%2+2)%2== a[j]); }else{ tmp &= (((sum + su[i+j]) % 2+2)%2 == a[j]); } } A2[sum][in]+=tmp; } } } return dfs(1, 0, 0, 1); } int main(){ scanf("%d", &T); for(int i = 0 ; i < 512 ; ++i){ int tmp = i; while(tmp){ su[i] += (tmp & 1); tmp >>= 1; } } while(T--){ memset(dp, -1, sizeof(dp)); memset(A1,0,sizeof(A1)); memset(A2,0,sizeof(A2)); scanf("%d%lld", &m, &L); lev = L % (1 << 8); for(int i = 0 ; i < m ; ++i) scanf("%d", &a[i]); printf("%lld\n", calc(L)); } return 0; }