洛谷P4067 储能表
P4067 储能表
题意:给定n,m,k,求
\[\sum_{i=0}^{n-1} \sum_{j=0}^{m-1} \mathrm{max} ((i \mathrm{xor} j)-k,0)
\]
定义状态为\(f[枚举了[0,cur)位][已枚举的位中n'是否与n相同(否则更小)][m同理][k'是否与k相同(否则更大)]\)
#include <bits/stdc++.h>
#define all(x) begin(x), end(x)
#define _for(i,f,t) for (int i = (f); i < (t); ++i)
#define _rep(i,f,t) for (int i = (f); i <= (t); ++i)
#define rd(...) __VA_ARGS__; ran(__VA_ARGS__);
using namespace std;
using ll = long long;
template<class T> int size(T& x) {return int(x.size());}
template<class... A> void ran(A&... a) { (int[]){(cin>> a,0)...}; }
template<class... A> void od(A&&... a) { (int[]){(cout<< a<< ' ',0)...}; } void odn() { cout<< '\n';}
template<class T, class... A> void odn(T&& x, A&&... a) { cout<<x; (int[]){(cout<< ' '<< a,0)...}; cout<<'\n'; }
#define int ll
int P;
struct modint {
using mint = modint;
int x; // assume 0 <= x < P 外部最多只读,不要修改
constexpr modint() : x(0) {}
modint(ll x) : x(mod(x)) {}
// modint(int x) : x(mod(x)) {}
template<class T>
static inline int mod(T x) { return x>=0&&x<P ? x : ((x%=P) >= 0 ? x : x + P);} // no assume
mint &operator*=(const mint &rhs) { x = ll(x) * rhs.x % P; return *this;}
mint &operator+=(const mint &rhs) { if ((x += rhs.x) >= P) x -= P; return *this;}
mint &operator-=(const mint &rhs) { if ((x -= rhs.x) < 0) x += P; return *this;}
mint &operator/=(const mint &rhs) { return *this *= rhs.inv();}
mint operator-() const { return mint(P - x); }
mint pow(ll b) const {mint ans(1), a(*this); for(; b; b>>=1, a *= a) if(b&1) ans *= a; return ans;}
mint inv() const { assert(x != 0); return pow(P - 2);}
int val() const {return x;}
friend mint operator*(const mint &lhs,const mint &rhs){return mint(lhs) *= rhs;}
friend mint operator+(const mint &lhs,const mint &rhs){return mint(lhs) += rhs;}
friend mint operator-(const mint &lhs,const mint &rhs){return mint(lhs) -= rhs;}
friend mint operator/(const mint &lhs,const mint &rhs){return mint(lhs) /= rhs;}
friend ostream &operator<<(ostream &os, const mint &a) { return os << a.x;}
friend istream &operator>>(istream &is, mint &a) {ll x; is>> x; a=mint(x); return is;}
};
using mint = modint;
// const int maxl = 2;
const int maxl = 60+5;
struct Node {mint num, sum;};
int dig1[maxl], dig2[maxl], dig3[maxl];
pair<bool, Node> memo[maxl][2][2][2];
// 目前枚举了[0,cur)位,lim1则都刚好贴n(否则小于k),lim2同理,lim3为是否正好贴k(否则已经超过k),组成大于k的{对数,和}
Node F(int cur, bool lim1, bool lim2, bool lim3) {
if (cur == maxl) return Node{1, 0};
auto& mem = memo[cur][lim1][lim2][lim3];
if (mem.first) return mem.second;
mem.first = true;
auto& ans = mem.second = {0,0};
int mx1 = lim1 ? dig1[cur] : 1,
mx2 = lim2 ? dig2[cur] : 1;
_rep(i, 0, mx1) _rep(j, 0, mx2) {
if (lim3 && (i^j) < dig3[cur]) continue;
auto t = F(cur+1, lim1 && i == mx1, lim2 && j == mx2, lim3 && (i^j) == dig3[cur]);
ans.num += t.num;
ans.sum += (t.sum) + mint(i^j)*(1ll<<(maxl-1-cur)) * t.num;
}
return ans;
}
mint getpref(ll n, ll m, ll k) {
memset(memo, 0, sizeof(memo));
int cnt;
memset(dig1, 0, sizeof(dig1)); cnt = maxl-1; for (auto tn=n; tn; tn /= 2) dig1[cnt--] = tn % 2;
memset(dig2, 0, sizeof(dig1)); cnt = maxl-1; for (auto tm=m; tm; tm /= 2) dig2[cnt--] = tm % 2;
memset(dig3, 0, sizeof(dig1)); cnt = maxl-1; for (auto tk=k; tk; tk /= 2) dig3[cnt--] = tk % 2;
Node ans = F(0, true, true, true);
return ans.sum - ans.num * k;
}
void solve() {
ll rd(n, m, k); cin>> P;
odn(getpref(n-1, m-1, k));
}
signed main() {
#ifdef _DEBUG
freopen("i", "r", stdin);
// freopen("o", "w", stdout);
#else
cout.tie(0), cin.tie(0)->sync_with_stdio(0);
#endif
int rd(T); while (T--)
solve();
}
讨论记忆化复杂度
在本题中,后面三维记忆话中均不可省略。
省略后的转变:不限制转移到不限制决策方法为k,限制到不限制决策a个,那么由复杂度O(1)变成\(O(a * k^{maxl})\)。
因此,如果要省略记忆某一维,必须要求k=1且a不大。总结,为了方便还是全记忆化吧。
因此本题,没有一维满足这样的限制,所以都一定不可省略记忆。
如果不记忆部分变量,在多组下也不会优化时间。因为状态这样设计,必须要充分描述。可以考虑换状态设计,尽可能少limit,而将状态描述成位都任意取,并考虑减小过程变量,让过程在枚举每位中记录(见“多数据无关写法”)。

浙公网安备 33010602011771号