1. 程式人生 > >4513: [Sdoi2016]儲能表

4513: [Sdoi2016]儲能表

map bsp git turn 分析 dfs style 這一 algo

4513: [Sdoi2016]儲能表

鏈接

分析:

  數位dp。

  橫坐標和縱坐標一起數位dp,分別記錄當前橫縱坐標中這一位是否受n或m的限制,在記錄一維表示當前是否已經大於k了。

  然後需要兩個數組記錄答案,分別記錄個數和答案的和。

  語意不清了。。。看代碼吧。。

代碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include
<set> #include<vector> #include<queue> #include<map> #define fi(s) freopen(s,"r",stdin); #define fo(s) freopen(s,"w",stdout); using namespace std; typedef long long LL; inline LL read() { LL x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1
; for(;isdigit(ch);ch=getchar())x=x*10+ch-0;return x*f; } const int N = 1005; int a[N], b[N], c[N], c1, c2, c3, Cnt; LL dp[N][2][2][2], mi[N], cnt[N][2][2][2], n, m, k, p; #define pa pair<LL,LL> pa dfs(int x,LL now,bool l1,bool l2,bool l3) { if (!x) { return pa((-k + p) % p, 1
); } if (dp[x][l3][l1][l2]) return pa(dp[x][l3][l1][l2], cnt[x][l3][l1][l2]); int u1 = l1 ? a[x] : 1; int u2 = l2 ? b[x] : 1; LL res = 0, sum = 0; for (int i = 0; i <= u1; ++i) for (int j = 0; j <= u2; ++j) { int t = i ^ j; if (l3 && t < c[x]) continue; pa tmp = dfs(x - 1, t ? now + mi[x - 1] : now, l1 && i == u1, l2 && j == u2, l3 && t == c[x]); res += (tmp.first + tmp.second * t * mi[x - 1] % p) % p; sum += tmp.second; res %= p; sum %= p; } dp[x][l3][l1][l2]= res, cnt[x][l3][l1][l2] = sum; return pa(res, sum); } void Calc() { n --, m --; c1 = c2 = c3 = Cnt = 0; LL t = n; while (t) a[++c1] = t % 2, t /= 2; t = m; while (t) b[++c2] = t % 2, t /= 2; t = k; while (t) c[++c3] = t % 2, t /= 2; Cnt = max(c1, max(c2, c3)); cout << dfs(Cnt, 0, 1, 1, 1).first << "\n"; for (int i = 0; i <= Cnt; ++i) a[i] = b[i] = c[i] = 0; memset(dp, 0, sizeof(dp)); memset(cnt, 0, sizeof(cnt)); } void solve() { n = read(), m = read(), k = read(), p = read(); mi[0] = 1; for (int i = 1; i <= 100; ++i) mi[i] = mi[i - 1] * 2 % p; Calc(); } int main() { for (int T = read(); T --; solve()); return 0; }

4513: [Sdoi2016]儲能表