BZOJ4513: [Sdoi2016]储能表(数位dp)

匿名 (未验证) 提交于 2019-12-02 23:03:14

题目链接

Sol

一点思路都没有,只会暴力,没想到标算是数位dp??Orz

首先答案可以分成两部分来统计

\[ f_{i,j}= \begin{aligned} i\oplus j &\left( i\oplus j >k\right) \\ 0 &\left( i\oplus j <=k\right) \end{aligned} \]

那么我们要求的就是

\[\sum_{i=0}^{n - 1} \sum_{j = 0}^{m - 1} f(i, j) - k * \sum_{i = 0}^{n - 1} \sum_{j = 0}^{m - 1} [f(i, j)]\]

也就是说,我们要统计出满足条件的数的异或和以及满足条件的数的对数

考虑直接在二进制下数位dp,注意这里我们要记三维状态

\(f[len][0/1][0/1][0/1]\)表示此时到第\(len\)位,是否顶着\(n\)的上界,是否顶着\(m\)的上界,是否顶着\(k\)的下界

然后直接dp就可以了

// luogu-judger-enable-o2 #include<bits/stdc++.h> #define Pair pair<LL, LL> #define MP make_pair  #define fi first #define se second  #define LL long long  #define int long long  using namespace std; const int MAXN = 233; inline LL read() {     char c = getchar(); int x = 0, f = 1;     while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();     return x * f; } LL N, M, K, mod, Lim, vis[MAXN][2][2][2]; Pair f[MAXN][2][2][2]; void add2(LL &x, LL y) {     if(x + y < 0) x = (x + y + mod);     else x = (x + y >= mod ? x + y - mod : x + y); } LL add(LL x, LL y) {     if(x + y < 0) return x + y + mod;     return x + y >= mod ? x + y - mod : x + y; } LL mul(LL x, LL y) {     return 1ll * x % mod * y % mod; } int Get(LL x) {     int len = 0; while(x) x >>= 1, len++; return len; } Pair dfs(int now, int f1, int f2, int f3) {     if(now > Lim) return MP(0, 1);     if(vis[now][f1][f2][f3]) return f[now][f1][f2][f3];     vis[now][f1][f2][f3] = 1;     Pair ans = MP(0, 0);     int L1 = (N >> Lim - now) & 1, L2 = (M >> Lim - now) & 1, L3 = (K >> Lim - now) & 1;     //cout << (f1 &&(!L1)) << endl;     for(int i = 0; i <= (f1 ? L1 : 1); i++) {         for(int j = 0; j <= (f2 ? L2 : 1); j++) {             if(f3 && ((i ^ j) < L3)) continue;             Pair nxt = dfs(now + 1, f1 && (i == L1), f2 && (j == L2), f3 && ((i ^ j) == L3));             add2(ans.se, nxt.se);             add2(ans.fi, add(nxt.fi, mul(nxt.se, mul((i ^ j), (1ll << Lim - now)))));         }     }     return f[now][f1][f2][f3] = ans; }  int solve() {     memset(vis, 0, sizeof(vis));     memset(f, 0, sizeof(f));     Lim = 0;     N = read(); M = read(); K = read(); mod = read(); N--; M--;     Lim = max(Get(N), max(Get(K), Get(M)));     Pair ans = dfs(1, 1, 1, 1);     return add(ans.fi, -mul(K, ans.se)); } signed main() {     for(int T = read(); T; T--, printf("%lld\n", solve()));     return 0; } /* 5000 504363800392059286 554192717354508770 21453916680846604 401134357 */ 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!