比赛链接 http://codeforces.com/gym/102832
A. Krypton 题意 充游戏币,首充可以获得优惠,之后充值就没有优惠了,问你x元最多能拿到多少游戏币。 $$ \begin{array}{|c|c|c|} \hline \text{Price (RMB yuan)} & \text{Normal amount (coupons)} & \text{First recharge reward (coupons)} \ \hline 1 & 10 & 8 \ \hline 6 & 60 & 18 \ \hline 28 & 280 & 28 \ \hline 88 & 880 & 58 \ \hline 198 & 1980 & 128 \ \hline 328 & 3280 & 198 \ \hline 648 & 6480 & 388 \ \hline \end{array} $$
题解 只考虑首充,这个题目本质上就是一个01背包,考虑其他充值,是一个完全背包。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <bits/stdc++.h> using namespace std;typedef long long ll;ll f[10 ][2100 ]; int main () { ll n; scanf ("%lld" , &n); vector<pair<ll , ll >> packags; packags.push_back (make_pair (1 , 18 )); packags.push_back (make_pair (6 , 78 )); packags.push_back (make_pair (28 , 308 )); packags.push_back (make_pair (88 , 938 )); packags.push_back (make_pair (198 , 2108 )); packags.push_back (make_pair (328 , 3478 )); packags.push_back (make_pair (648 , 6868 )); for (ll i = 0 ; i < packags.size (); ++i) { for (ll j = 0 ; j <= n; ++j) { ll v = packags[i].first, w = packags[i].second; if (j >= v && i >= 1 ) { f[i][j] = max (f[i - 1 ][j], f[i - 1 ][j - v] + w); } else if (i >= 1 ) { f[i][j] = f[i - 1 ][j]; } else { f[i][j] = (j >= v) ? w : 0 ; } } } ll ans = 0 ; for (ll i = 0 ; i <= n; ++i) { ans = max (ans, f[6 ][i] + (n - i) * 10 ); } printf ("%lld\n" , ans); }
D. Meaningless Sequence 题意 $$ a_n = \begin{cases} 1, & n = 0 \ c \cdot \max\limits_{0 \leq i < n} a_{n \operatorname{&} i}, & \text{otherwise} \end{cases}, $$
你要计算 $$ \left( \sum\limits_{i=0}^n a_i \right) \bmod (10^9+7) $$
题解 $a_i$与数字i的二进制表示法中有多少个1有关,如果有k个,则为c的k次方,直接数位dp即可。
http://codeforces.com/gym/102832/submission/115385503
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <bits/stdc++.h> using namespace std;typedef long long ll;char s[3010 ];const int mod = 1e9 + 7 ;int qpow (int a, int b) { int res = 1 ; while (b) { if (b & 1 ) { res = 1ll * res * a % mod; } a = 1ll * a * a % mod; b >>= 1 ; } return res; } int dp[2 ][3010 ][3010 ];bool vis[2 ][3010 ][3010 ];int count (int cur, int n, int limit, int oneCount, int zeroCount) { if (vis[limit][oneCount][zeroCount]) { return dp[limit][oneCount][zeroCount]; } int ans = 0 ; if (cur == n) { return 1 ; } if (zeroCount != 0 ) { ans = (ans + count (cur + 1 , n, limit && s[cur] == '0' , oneCount, zeroCount - 1 )) % mod; } if (oneCount != 0 ) { if (!limit || s[cur] != '0' ) { ans = (ans + count (cur + 1 , n, limit && s[cur] == '1' , oneCount - 1 , zeroCount)) % mod; } } vis[limit][oneCount][zeroCount] = true ; return dp[limit][oneCount][zeroCount] = ans; } int main () { int c; scanf ("%s %d" , s, &c); int n = strlen (s); int ans = 0 ; for (int i = 0 ; i <= n; i++) { int cnt = count (0 , n, true , i, n - i); ans = (ans + 1ll * cnt * qpow (c, i)) % mod; } printf ("%d\n" , ans); }
F. Strange Memory 题意 给你一颗点带权的树,你要计算,其中$\oplus$表示异或 $$ \sum\limits_{i=1}^n\sum\limits_{j=i+1}^n [a_i \oplus a_j = a_{\operatorname{lca}(i, j)}] (i \oplus j). $$
题解 暴力枚举lca,如果解决了一些子树的子问题,那么合并到父节点的时候,只需要枚举不同子树里面有多少个数满足点权异或和等于父节点值。
很明显这里是三个变量,但是当枚举lca时,父节点值为定值,所以实际上只有两个变量,由于异或构成群,于是可以枚举一个变量,寻找另一个变量,很自然想到了枚举小的子树,在大的子树中寻找,这实际上就是树上启发式合并。
http://codeforces.com/gym/102832/submission/115392207
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 #include <bits/stdc++.h> using namespace std;typedef long long ll;const ll maxn = 1e5 + 100 ;ll a[maxn], sz[maxn]; vector<ll> g[maxn]; struct Node { vector<ll> bit0, bit1; vector<ll> lazy; Node () : bit0 (17 ), bit1 (17 ) {} ll query (ll x) { for (ll tmp:lazy) { lazyInsert (tmp); } lazy.clear (); ll res = 0 ; for (ll i = 0 ; i < 17 ; ++i) { if ((1 << i) & x) { res += bit0[i]; } else { res += bit1[i]; } } return res; } void insert (ll x) { lazy.push_back (x); } void lazyInsert (ll x) { for (ll i = 0 ; i < 17 ; ++i) { if ((1 << i) & x) { bit1[i] += (1 << i); } else { bit0[i] += (1 << i); } } } }; struct DS { vector<pair<ll, ll >> kv; ll ans = 0 ; unordered_map<ll, Node> ma; ll query (ll k, ll v) { auto tmp = ma.find (v); return tmp == ma.end () ? 0 : tmp->second.query (k); } void insert (ll k, ll v) { kv.push_back ({k, v}); ma[v].insert (k); } }; void dfs (ll u, ll f) { sz[u] = 1 ; for (auto v : g[u]) { if (v == f) { continue ; } dfs (v, u); sz[u] += sz[v]; } } void dfs2 (ll u, ll f, DS &ds) { ll mxv = 0 ; for (auto v : g[u]) { if (v == f) { continue ; } if (sz[v] > sz[mxv]) { mxv = v; } } if (mxv != 0 ) { dfs2 (mxv, u, ds); ll ans = ds.ans; for (auto v : g[u]) { if (v == f || v == mxv) { continue ; } DS tem; dfs2 (v, u, tem); ans += tem.ans; for (auto x : tem.kv) { ans += ds.query (x.first, x.second ^ a[u]); } for (auto x: tem.kv) { ds.insert (x.first, x.second); } } ds.ans = ans; } ds.insert (u, a[u]); } int main () { ll n; scanf ("%lld" , &n); for (ll i = 1 ; i <= n; ++i) { scanf ("%lld" , &a[i]); } for (ll i = 1 ; i <= n - 1 ; ++i) { ll u, v; scanf ("%lld %lld" , &u, &v); g[u].push_back (v); g[v].push_back (u); } dfs (1 , 0 ); DS ds; dfs2 (1 , 0 , ds); printf ("%lld\n" , ds.ans); }
K. Ragdoll 题意 给你一个森林,每个点有一个权,有三个操作,
增加一个单个节点的树
合并两颗树
修改一颗树的某个节点的权
每次操作以后你要输出有多少对节点在同一棵树且$gcd(i,j)=i\oplus j$
题解 预处理出所有$gcd(i,j)=i\oplus j$的数对,然后启发式合并。