Atcoder Educational DP Contest

A - Frog 1

枚举前继直接转移即可。

复制代码
 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 }
View Code
复制代码

B - Frog 2

做法同上。

复制代码
 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 }
View Code
复制代码

C - Vacation

由于有相邻两个活动不能相同的限制,在转移时特殊判断即可。

复制代码
 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 }
View Code
复制代码

D - Knapsack 1

经典背包,需要注意转移时的枚举顺序,不可做成完全背包。

复制代码
 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 }
View Code
复制代码

E - Knapsack 2

由于重量值域很大,无法将重量作为维数背包。观察到价值非常小,考虑将价值作为维数,将重量作为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 }
View Code
复制代码

F - LCS

经典题。然而此题需要输出具体字符串。根据成功转移时的性质逆序处理即可。

复制代码
 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 }
View Code
复制代码

G - Longest Path

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 }
View Code
复制代码

H - Grid 1

经典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 }
View Code
复制代码

I - Coins

我们只关心正面币的数量与反面币的数量的差具体是多少。因此可以由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 }
View Code
复制代码

 J - Sushi

一开始遇到这题的时候没找到好的切入点,仔细思考后其实我们并不关心当前具体选了哪个位置,只关系当前选的位置是什么状态。考虑当前局面共有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 }
View Code
复制代码

K - Stones

博弈论,初始的必败局面为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 }
View Code
复制代码

L - Deque

类似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 }
View Code
复制代码

M - Candies

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 }
View Code
复制代码

N - Slimes

和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 }
View Code
复制代码

O - Matching

状压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 }
View Code
复制代码

P - Independent Set

树形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 }
View Code
复制代码

Q - Flowers

数据结构优化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 }
View Code
复制代码

R - Walk

矩阵快速幂。初始状态为单位矩阵,转移矩阵即为邻接矩阵,由于需要转移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 }
View Code
复制代码

S - Digit Sum

数位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 }
View Code
复制代码

U - Grouping

状压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 }
View Code
复制代码

 

posted @   Frozen-code  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示