Atcoder Educational DP Contest
枚举前继直接转移即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 500010; 13 const ll INF = 2147483647; 14 ll n, a[maxn], dp[maxn]; 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> n; 19 for(int i = 1; i <= n; i++) cin >> a[i]; 20 memset(dp, 0x3f, sizeof(dp)); 21 dp[1] = 0; 22 for(int i = 2; i <= n; i++){ 23 for(int j = max(1, i - 2); j < i; j++) dp[i] = min(dp[i], dp[j] + abs(a[i] - a[j])); 24 } 25 cout << dp[n]; 26 return 0; 27 }
做法同上。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 500010; 13 const ll INF = 2147483647; 14 ll n, k, a[maxn], dp[maxn]; 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> n >> k; 19 for(int i = 1; i <= n; i++) cin >> a[i]; 20 memset(dp, 0x3f, sizeof(dp)); 21 dp[1] = 0; 22 for(int i = 2; i <= n; i++){ 23 for(int j = max(1ll * 1, i - k); j < i; j++) dp[i] = min(dp[i], dp[j] + abs(a[i] - a[j])); 24 } 25 cout << dp[n]; 26 return 0; 27 }
由于有相邻两个活动不能相同的限制,在转移时特殊判断即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 500010; 13 const ll INF = 2147483647; 14 struct node{ 15 ll a, b, c; 16 }a[maxn]; 17 ll res, n, dp[maxn][5]; 18 int main() 19 { 20 ios::sync_with_stdio(false); 21 cin >> n; 22 for(int i = 1; i <= n; i++) cin >> a[i].a >> a[i].b >> a[i].c; 23 for(int i = 1; i <= n; i++){ 24 dp[i][1] = max(dp[i][1], max(dp[i - 1][2], dp[i - 1][3]) + a[i].a); 25 dp[i][2] = max(dp[i][2], max(dp[i - 1][1], dp[i - 1][3]) + a[i].b); 26 dp[i][3] = max(dp[i][3], max(dp[i - 1][2], dp[i - 1][1]) + a[i].c); 27 if(i == n) res = max(dp[n][1], max(dp[n][2], dp[n][3])); 28 } 29 cout << res; 30 return 0; 31 }
经典背包,需要注意转移时的枚举顺序,不可做成完全背包。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 100010; 13 const ll INF = 2147483647; 14 ll n, w, a[maxn], b[maxn], dp[maxn]; 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> n >> w; 19 for(int i = 1; i <= n; i++){ 20 cin >> a[i] >> b[i]; 21 for(int j = 0; j + a[i] <= w; j++) dp[j] = max(dp[j], dp[j + a[i]] + b[i]); 22 } 23 cout << dp[0]; 24 return 0; 25 }
由于重量值域很大,无法将重量作为维数背包。观察到价值非常小,考虑将价值作为维数,将重量作为dp值。一个显然的性质是此时dp值随价值单调上升。用dp值不大于限制的价值更新答案即可。为了方便转移使用了滚动数组。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 100010; 13 const ll INF = 2147483647; 14 ll n, w, a[maxn], b[maxn], dp1[maxn], dp2[maxn]; 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> n >> w; 19 memset(dp1, 0x3f, sizeof(dp1)); 20 memset(dp2, 0x3f, sizeof(dp2)); 21 dp2[0] = 0; 22 for(int i = 1; i <= n; i++){ 23 cin >> a[i] >> b[i]; 24 for(int j = b[i]; j < maxn; j++) dp1[j] = min(dp1[j], dp2[j - b[i]] + a[i]); 25 for(int j = 0; j < maxn; j++) dp2[j] = min(dp1[j], dp2[j]); 26 } 27 ll res = 0; 28 for(int j = 0; j < maxn; j++){ 29 if(dp1[j] <= w){ 30 res = max(res, 1ll * j); 31 } 32 } 33 cout << res; 34 return 0; 35 }
经典题。然而此题需要输出具体字符串。根据成功转移时的性质逆序处理即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 3010; 13 const ll INF = 2147483647; 14 string a, b, ans; 15 ll x, y, dp[maxn][maxn]; 16 int main() 17 { 18 cin >> a >> b; 19 for(int i = 1; i <= a.length(); i++){ 20 for(int j = 1; j <= b.length(); j++){ 21 if(a[i - 1] == b[j - 1]){ 22 dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1); 23 } 24 else{ 25 dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]); 26 } 27 } 28 } 29 ll x = a.length(), y = b.length(); 30 while(x > 0 && y > 0){ 31 if(a[x - 1] == b[y - 1]){ 32 ans.pb(a[x - 1]); 33 x--; 34 y--; 35 } 36 else if(dp[x][y] == dp[x - 1][y]){ 37 x--; 38 } 39 else{ 40 y--; 41 } 42 } 43 reverse(ans.begin(), ans.end()); 44 for(auto s : ans) cout << s; 45 return 0; 46 }
DAG上拓扑排序后dp。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 500010; 13 const ll INF = 2147483647; 14 ll n, m, x, y, res, deg[maxn], dp[maxn]; 15 ll tot, head[maxn], nxt[maxn], edge[maxn]; 16 void add_edge(ll x, ll y){ 17 nxt[++tot] = head[x]; 18 edge[tot] = y; 19 head[x] = tot; 20 } 21 int main() 22 { 23 ios::sync_with_stdio(false); 24 cin >> n >> m; 25 for(int i = 1; i <= m; i++){ 26 cin >> x >> y; 27 deg[y]++; 28 add_edge(x, y); 29 } 30 queue <ll> q; 31 for(int i = 1; i <= n; i++) if(!deg[i]) q.push(i); 32 while(!q.empty()){ 33 ll tem = q.front(); 34 q.pop(); 35 for(int i = head[tem]; i; i = nxt[i]){ 36 ll y = edge[i]; 37 dp[y] = max(dp[y], dp[tem] + 1); 38 res = max(res, dp[y]); 39 if(--deg[y] == 0) q.push(y); 40 } 41 } 42 cout << res; 43 return 0; 44 }
经典dp,遇到障碍不转移即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 1010; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 ll n, m, dp[maxn][maxn]; 16 char ch[maxn][maxn]; 17 ll add(ll x, ll y){return (x + y) % mod;} 18 int main() 19 { 20 ios::sync_with_stdio(false); 21 cin >> n >> m; 22 dp[1][1] = 1; 23 for(int i = 1; i <= n; i++){ 24 for(int j = 1; j <= m; j++){ 25 cin >> ch[i][j]; 26 if(ch[i][j] == '.'){ 27 if(j > 1) dp[i][j] = add(dp[i][j], dp[i][j - 1]); 28 if(i > 1) dp[i][j] = add(dp[i][j], dp[i - 1][j]); 29 } 30 } 31 } 32 cout << dp[n][m]; 33 return 0; 34 }
我们只关心正面币的数量与反面币的数量的差具体是多少。因此可以由n-1个币的局面递推到n个币的局面,枚举第n个币是正面还是反面转移即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 3010; 13 const ll INF = 2147483647; 14 ll n; 15 double x, res, dp[maxn][6020]; 16 int main() 17 { 18 ios::sync_with_stdio(false); 19 cin >> n; 20 dp[0][3000] = 1; 21 for(int i = 1; i <= n; i++){ 22 cin >> x; 23 for(int j = 3000 - i; j <= 3000 + i; j++){ 24 dp[i][j] += dp[i - 1][j - 1] * x; 25 dp[i][j] += dp[i - 1][j + 1] * (1 - x); 26 if(i == n && j > 3000) res += dp[i][j]; 27 } 28 } 29 printf("%.9f\n", res); 30 return 0; 31 }
一开始遇到这题的时候没找到好的切入点,仔细思考后其实我们并不关心当前具体选了哪个位置,只关系当前选的位置是什么状态。考虑当前局面共有a个1、b个2、c个3,则我们选到空位置、1、2、3的概率都是已知的,由此可以列出递推式记忆化搜索。(n的范围也暗示着是三个状态)

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 310; 13 const ll INF = 2147483647; 14 ll n, a[maxn], cnt[maxn]; 15 double res, dp[maxn][maxn][maxn]; 16 bool vis[maxn][maxn][maxn]; 17 double solve(ll cnt1, ll cnt2, ll cnt3){ 18 if(vis[cnt1][cnt2][cnt3]) return dp[cnt1][cnt2][cnt3]; 19 if(cnt1 == 0 && cnt2 == 0 && cnt3 == 0) return 0; 20 double tem = n; 21 if(cnt1) tem += 1.0 * cnt1 * solve(cnt1 - 1, cnt2, cnt3); 22 if(cnt2) tem += 1.0 * cnt2 * solve(cnt1 + 1, cnt2 - 1, cnt3); 23 if(cnt3) tem += 1.0 * cnt3 * solve(cnt1, cnt2 + 1, cnt3 - 1); 24 tem /= 1.0 * (cnt1 + cnt2 + cnt3); 25 vis[cnt1][cnt2][cnt3] = 1; 26 return dp[cnt1][cnt2][cnt3] = tem; 27 } 28 int main() 29 { 30 ios::sync_with_stdio(false); 31 cin >> n; 32 for(int i = 1; i <= n; i++){ 33 cin >> a[i]; 34 cnt[a[i]]++; 35 } 36 res = solve(cnt[1], cnt[2], cnt[3]); 37 printf("%.9f\n", res); 38 return 0; 39 }
博弈论,初始的必败局面为0到Min{a_i},对于之后的局面可以枚举集合A中的数转移后继,若后继存在必败态则当前状态为必胜态,否则为必败态。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 100010; 13 const ll INF = 2147483647; 14 ll n, k, dp[maxn], a[maxn]; 15 int main() 16 { 17 ios::sync_with_stdio(false); 18 cin >> n >> k; 19 for(int i = 1; i <= n; i++) cin >> a[i]; 20 for(int i = 0; i < a[1]; i++) dp[i] = 0; 21 for(int i = a[1]; i <= k; i++){ 22 ll tem = 0; 23 for(int j = 1; j <= n; j++){ 24 if(i - a[j] < 0) break; 25 tem |= !dp[i - a[j]]; 26 } 27 dp[i] = tem; 28 } 29 if(dp[k]) cout << "First"; 30 else cout << "Second"; 31 return 0; 32 }
类似Min_Max搜索,先手转移时取后继的Max,后手时当前选取的数贡献取负且取后继的Min。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 3010; 13 const ll INF = 21474836470000000; 14 ll n, a[maxn], dp[maxn][maxn][2]; 15 ll solve(ll l, ll r, ll op){ 16 if(dp[l][r][op] != INF) return dp[l][r][op]; 17 if(l == r) return dp[l][r][op] = (op ? -1 : 1) * a[l]; 18 if(!op) return dp[l][r][op] = max(solve(l, r - 1, op ^ 1) + (op ? -1 : 1) * a[r], solve(l + 1, r, op ^ 1) + (op ? -1 : 1) * a[l]); 19 else return dp[l][r][op] = min(solve(l, r - 1, op ^ 1) + (op ? -1 : 1) * a[r], solve(l + 1, r, op ^ 1) + (op ? -1 : 1) * a[l]); 20 } 21 int main() 22 { 23 ios::sync_with_stdio(false); 24 cin >> n; 25 for(int i = 1; i <= n; i++) cin >> a[i]; 26 for(int i = 1; i <= n; i++){ 27 for(int j = i; j <= n; j++) dp[i][j][0] = dp[i][j][1] = INF; 28 } 29 // cout << dp[0][0][0] << " " << 0x3f3f3f3f3f3f3f3f; 30 cout << solve(1, n, 0); 31 return 0; 32 }
dp[i][j]表示分到第i个小孩后已经分了j个糖果的方案。从i-1转移到i时状态j只能由max{0, j - a[i]}到j的前继转移。使用类似滑动窗口的trick可以从O(nk^2)优化到O(nk)。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 100010; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 ll add(ll x, ll y){return (x + y) % mod;} 16 ll sub(ll x, ll y){return (x + mod - y) % mod;} 17 ll n, k, res, a[maxn], dp[105][maxn]; 18 int main() 19 { 20 ios::sync_with_stdio(false); 21 cin >> n >> k; 22 for(int i = 1; i <= n; i++) cin >> a[i]; 23 dp[0][0] = 1; 24 for(int i = 1; i <= n; i++){ 25 ll sum = 0; 26 for(int j = 0; j <= k; j++){ 27 sum = add(sum, dp[i - 1][j]); 28 if(j > a[i]) sum = sub(sum, dp[i - 1][j - a[i] - 1]); 29 dp[i][j] = sum; 30 } 31 } 32 cout << dp[n][k]; 33 return 0; 34 }
和NOI1995石子合并差不多。区间dp,枚举大区间分裂到两个小区间的分裂点计算代价取Min转移即可。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 510; 13 const ll INF = 2147483647000000; 14 ll n, a[maxn], pre[maxn], dp[maxn][maxn]; 15 ll solve(ll l, ll r){ 16 if(dp[l][r] != 0) return dp[l][r]; 17 if(l == r) return dp[l][r] = 0; 18 ll tem = INF; 19 for(int i = l; i < r; i++){ 20 tem = min(tem, solve(l, i) + solve(i + 1, r) + pre[r] - pre[l - 1]); 21 } 22 return dp[l][r] = tem; 23 } 24 int main() 25 { 26 ios::sync_with_stdio(false); 27 cin >> n; 28 for(int i = 1; i <= n; i++) cin >> a[i]; 29 for(int i = 1; i <= n; i++) pre[i] = pre[i - 1] + a[i]; 30 cout << solve(1, n); 31 return 0; 32 }
状压dp。将剩下女士的状态用一个不大于2^n的数描述,该数第i位为1代表第i个女士尚未配对。枚举男士依次转移即可,需要注意第i个男士能和哪些女士匹配。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 50; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 ll n, res, a[maxn][maxn], dp[25][(1 << 22)]; 16 ll add(ll x, ll y){return (x + y) % mod;} 17 ll solve(ll pos, ll st){ 18 if(dp[pos][st] != -1) return dp[pos][st]; 19 ll tem = 0; 20 for(int j = 1; j <= n; j++){ 21 if((st >> (j - 1)) & 1 && a[pos][j]){ 22 tem = add(tem, solve(pos + 1, st - (1 << (j - 1)))); 23 } 24 } 25 return dp[pos][st] = tem; 26 } 27 int main() 28 { 29 ios::sync_with_stdio(false); 30 cin >> n; 31 for(int i = 1; i <= n; i++){ 32 for(int j = 1; j <= n; j++) cin >> a[i][j]; 33 } 34 memset(dp, -1, sizeof(dp)); 35 dp[n + 1][0] = 1; 36 res = solve(1, (1 << n) - 1); 37 cout << res; 38 return 0; 39 }
树形dp。dp[i][0/1]表示i点是白色/黑色的方案。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 500010; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 ll n, x, y, dp[maxn][2]; 16 ll tot, nxt[maxn], edge[maxn], head[maxn]; 17 bool vis[maxn]; 18 ll add(ll x, ll y){return (x + y) % mod;} 19 ll mul(ll x, ll y){return x * y % mod;} 20 void add_edge(ll x, ll y){ 21 nxt[++tot] = head[x]; 22 edge[tot] = y; 23 head[x] = tot; 24 } 25 void dfs(ll x){ 26 vis[x] = 1; 27 dp[x][0] = dp[x][1] = 1; 28 for(int i = head[x]; i; i = nxt[i]){ 29 ll tem = edge[i]; 30 if(!vis[tem]){ 31 dfs(tem); 32 dp[x][0] = mul(dp[x][0], add(dp[tem][0], dp[tem][1])); 33 dp[x][1] = mul(dp[x][1], dp[tem][0]); 34 } 35 } 36 } 37 int main() 38 { 39 ios::sync_with_stdio(false); 40 cin >> n; 41 for(int i = 1; i < n; i++){ 42 cin >> x >> y; 43 add_edge(x, y); 44 add_edge(y, x); 45 } 46 dfs(1); 47 cout << add(dp[1][0], dp[1][1]); 48 return 0; 49 }
数据结构优化dp。按出现顺序依次枚举,每次选取小于当前高度的最大dp值转移,用线段树实现区间Max。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 200010; 13 const ll INF = 2147483647; 14 ll n, res, s[maxn << 2]; 15 pii a[maxn]; 16 ll ls(ll p){return p << 1;} 17 ll rs(ll p){return p << 1 | 1;} 18 void upd(ll p, ll l, ll r, ll pos, ll val){ 19 if(l == r){ 20 s[p] = val; 21 return; 22 } 23 ll mid = (l + r) >> 1; 24 if(pos <= mid) upd(ls(p), l, mid, pos, val); 25 else upd(rs(p), mid + 1, r, pos, val); 26 s[p] = max(s[ls(p)], s[rs(p)]); 27 } 28 ll qry(ll p, ll l, ll r, ll nl, ll nr){ 29 if(nl > nr) return 0; 30 if(nl <= l && nr >= r) return s[p]; 31 ll res = 0; 32 ll mid = (l + r) >> 1; 33 if(nl <= mid) res = max(res, qry(ls(p), l, mid, nl, nr)); 34 if(nr > mid) res = max(res, qry(rs(p), mid + 1, r, nl, nr)); 35 return res; 36 } 37 int main() 38 { 39 ios::sync_with_stdio(false); 40 cin >> n; 41 for(int i = 1; i <= n; i++) cin >> a[i].fi; 42 for(int i = 1; i <= n; i++) cin >> a[i].se; 43 for(int i = 1; i <= n; i++){ 44 ll tem = qry(1, 1, n, 1, a[i].fi - 1) + a[i].se; 45 upd(1, 1, n, a[i].fi, tem); 46 res = max(res, tem); 47 } 48 cout << res; 49 return 0; 50 }
矩阵快速幂。初始状态为单位矩阵,转移矩阵即为邻接矩阵,由于需要转移K次且K很大,使用矩阵快速幂加速转移。答案即为最终矩阵各个位置的数的和。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 55; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 ll n, k, res, a[maxn][maxn]; 16 ll add(ll x, ll y){return (x + y) % mod;} 17 struct Mat{ 18 ll a[maxn][maxn]; 19 Mat(){ 20 memset(a,0,sizeof(a)); 21 } 22 inline void build(){ 23 for(int i=1;i<=n;i++)a[i][i]=1; 24 } 25 }; 26 Mat cur,ans; 27 Mat operator * (const Mat &x,const Mat &y){ 28 Mat z; 29 for(int i=1;i<=n;i++){ 30 for(int j=1;j<=n;j++){ 31 for(int k=1;k<=n;k++){ 32 z.a[j][k]=(z.a[j][k]+(x.a[j][i]*y.a[i][k]%mod))%mod; 33 } 34 } 35 } 36 return z; 37 } 38 int main() 39 { 40 ios::sync_with_stdio(false); 41 cin >> n >> k; 42 for(int i = 1; i <= n; i++){ 43 for(int j = 1; j <= n; j++) cin >> cur.a[i][j]; 44 } 45 ans.build(); 46 while(k){ 47 if(k & 1) ans = ans * cur; 48 cur = cur * cur; 49 k >>= 1; 50 } 51 // for(int i = 1; i <= n; i++){ 52 // for(int j = 1; j <= n; j++) cout << ans.a[i][j] << " \n"[j == n]; 53 // } 54 // for(int i = 1; i <= n; i++) res = add(res, ans.a[i][i]); 55 for(int i = 1; i <= n; i++){ 56 for(int j = 1; j <= n; j++) res = add(res, ans.a[i][j]); 57 } 58 cout << res; 59 return 0; 60 }
数位dp。状态为第pos位,当前数位和位sum,是否贴近上界的标志limit。注意需要去除0.

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 10010; 13 const ll INF = 2147483647; 14 const ll mod = 1e9 + 7; 15 string k; 16 ll d, len, res, dp[maxn][105][2]; 17 ll add(ll x, ll y){return (x + y) % mod;} 18 ll sub(ll x, ll y){return (x + mod - 1) % mod;} 19 ll dfs(ll pos, ll sum, ll limit){ 20 if(dp[pos][sum][limit] > -1) return dp[pos][sum][limit]; 21 if(pos == len) return dp[pos][sum][limit] = (sum == 0); 22 ll tem = 0, up = limit ? (k[pos] - '0') : 9; 23 for(int i = 0; i <= up; i++){ 24 tem = add(tem, dfs(pos + 1, (sum + i) % d, limit & (i == up))); 25 } 26 return dp[pos][sum][limit] = tem; 27 } 28 int main() 29 { 30 ios::sync_with_stdio(false); 31 cin >> k; 32 cin >> d; 33 memset(dp, -1, sizeof(dp)); 34 len = k.length(); 35 res = sub(dfs(0, 0, 1), 1); 36 cout << res; 37 return 0; 38 }
状压dp。预处理出每个分割集合产生的贡献后,3^n枚举子集直接转移。

1 /* 2 Author:Frozencode 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define pb push_back 7 #define mp make_pair 8 #define fi first 9 #define se second 10 typedef long long ll; 11 typedef pair<ll,ll> pii; 12 const ll maxn = 17; 13 const ll INF = 21474836470000; 14 ll n, val[1 << maxn], dp[1 << maxn], a[maxn][maxn]; 15 ll cal(ll x){ 16 vector <ll> t; 17 for(int i = 0; i < 17; i++) if((x >> i) & 1) t.pb(i + 1); 18 ll sum = 0; 19 for(int i = 0; i < t.size(); i++){ 20 for(int j = 0; j < i; j++) sum += a[t[i]][t[j]]; 21 } 22 return sum; 23 } 24 int main() 25 { 26 ios::sync_with_stdio(false); 27 cin >> n; 28 for(int i = 1; i <= n; i++){ 29 for(int j = 1; j <= n; j++) cin >> a[i][j]; 30 } 31 dp[0] = 0; 32 for(int i = 1; i < (1 << n); i++) val[i] = cal(i); 33 for(int i = 1; i < (1 << n); i++){ 34 ll tem = -INF; 35 for(int j = i; j > 0; j = (j - 1) & i){ 36 tem = max(tem, dp[i - j] + val[j]); 37 } 38 dp[i] = tem; 39 } 40 cout << dp[(1 << n) - 1]; 41 return 0; 42 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!