第一次拿金当然也要记录一下。济南站我们队被分到单独的机房打,愣是用了四台电脑监控四个人(包括志愿者)。
因为以往罚时爆炸的影响,正式赛开始后一直心想着求稳。 zzy \text{zzy} zzy 开局找到 M \text{M} M 题秒出做法后跟我讲,然后我听错直接 hack \text{hack} hack 了,遂把签到题抢了过来自己做。接着 zzy \text{zzy} zzy 开 G \text{G} G 题粗心 WA \text{WA} WA 了两发,顿时心凉了一截,倒是 xbx \text{xbx} xbx 直接过来先把 C \text{C} C 题给秒了,然后我才交了 M \text{M} M 题,紧接着 G G G 题也过了。这时过完前 3 3 3 题还是很快的,但是我们却清楚两发 WA \text{WA} WA 罚时已经落后了。接着跟 xbx \text{xbx} xbx 讨论了下 D \text{D} D 题,过的也不算太慢,但是因为一小时功夫绝大多数队伍都能签到四题,这期间 WA \text{WA} WA 过就很伤。接着由于没榜,我直接扎进数据结构 K \text{K} K 题, zzy \text{zzy} zzy 就去看构造 J \text{J} J 题,未果后跟 xbx \text{xbx} xbx 要了 A \text{A} A 题,认为矩阵相关可能是高斯消元向的题目就喂给我(因为这方面我相对懂的多),我拿过来一看 n 2 n^2 n2 个变元,列完方程再消,即使 bitset \text{bitset} bitset 优化也要 O ( n 5 ) O(n^5) O(n5) 。纸上列了下方程发现独立性后就直接上了,在 1.5h \text{1.5h} 1.5h 直接 1A \text{1A} 1A 过了,这时候已经稳了低罚时了,心情平复了一些。后面两小时我都没有有建设性的想法,期间 zzy \text{zzy} zzy 在构造题疯狂试探,也喂了 xbx \text{xbx} xbx 一道看着是数位 dp \text{dp} dp 的 L \text{L} L 题(因为他相对擅长)。最后我弃了 K \text{K} K 题,帮 xbx \text{xbx} xbx 确认了转移思路后提了几个修补的地方,让他自己调 L \text{L} L 题,又去帮忙 J \text{J} J 题的构造(提了个假做法后, zzy \text{zzy} zzy 改变做法,想到了正解方向,但是没时间了,只能交之前的瞎搞)。几乎放弃的时候, xbx \text{xbx} xbx 一声大吼,在封榜后过了 L \text{L} L 题,之后瞎搞怼 J \text{J} J 题,比赛结束也没过。
结束后刷榜一看, J \text{J} J 题和 L \text{L} L 题各过了 60 60 60 多人,而金牌就 35 35 35 个,只做出其中一题,心想已经没了。切换到排行榜的时候一惊,反复确认后发现还是金了! 6 6 6 题从二十多排到六十多名,看来是大部分队伍也只出了其中一道,甚至有不少队伍是 A \text{A} A 题没出的。最终 rk26 \text{rk26} rk26(去掉前面两个打星队伍),总共三发 WA \text{WA} WA(点名批评某队友,交错题还多 WA \text{WA} WA 一发),但还是靠罚时排到前排了。前期罚时终于没有猛跪,果然面对熟悉的东西才有足够自信在前期直接开掉,几天 cf \text{cf} cf 回炉也有不少作用。虽然其他题目没出也有点遗憾,但是总体来说还是庆幸留了个金尾。今年 EC \text{EC} EC 大概是去不了了,但省赛之类的还是可以继续玩玩的。
https://ac.nowcoder.com/acm/contest/10662
给定 n × n n \times n n×n 的矩阵 A , B A, B A,B,求满足 A × C = B ⋅ C A \times C = B \cdot C A×C=B⋅C 的矩阵 C C C 的个数,所有运算都在模 2 2 2 意义下。
1 ≤ n ≤ 200 , A i , j , B i , j ∈ { 0 , 1 } 1 \le n \le 200, A_{i, j}, B_{i, j} \in \{0, 1\} 1≤n≤200,Ai,j,Bi,j∈{0,1} 。
容易发现 A × C ⋅ , i = B ⋅ , i ⋅ C ⋅ , i A \times C_{\cdot,i} = B_{\cdot, i} \cdot C_{\cdot, i} A×C⋅,i=B⋅,i⋅C⋅,i ,即 C C C 的各列独立,故只要分别求 n n n 列的结果,再根据乘法原理相乘得到最后的答案。每列可以列出 n n n 条异或方程,高斯消元后自由元个数是 x x x 个,满足的列就有 2 x 2^x 2x 个。
#include
using namespace std;
typedef long long ll;
const int maxn = 2e2 + 5;
const int mod = 998244353;
struct Gauss{
int solve(bitset<maxn> A[], int n, int m){
int r = 0;
for(int c = 0; c < m && r < n; ++c, ++r){
int mx = r;
for(int i = r; i < n; ++i){
if(A[i][c]) { mx = i; break; }
}
if(!A[mx][c]) { --r; continue; }
swap(A[r], A[mx]);
for(int i = 0; i < n; ++i){
if(i == r || !A[i][c]) continue;
A[i] ^= A[r];
}
}
for(int i = r; i < n; ++i) if(A[i][m]) return -1;
return r;
}
} gs;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int n; cin >> n;
vector<ll> pw2(n + 1);
pw2[0] = 1;
for(int i = 1; i <= n; ++i) pw2[i] = pw2[i - 1] * 2 % mod;
vector<vector<int>> a(n, vector<int>(n)), b = a;
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j) cin >> a[i][j];
}
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j) cin >> b[i][j];
}
ll ret = 1;
for(int c = 0; c < n; ++c){
bitset<maxn> A[maxn];
for(int i = 0; i < n; ++i){
A[i].reset();
for(int j = 0; j < n; ++j){
A[i][j] = a[i][j];
}
A[i][i] = A[i][i] ^ b[i][c];
}
int rk = gs.solve(A, n, n);
if(rk == -1) { ret = 0; break; }
(ret *= pw2[n - rk]) %= mod;
}
cout << ret << endl;
return 0;
}
待补。
有若干堆石头,其中有 1 , 2 , 3 1, 2, 3 1,2,3 颗石头的分别有 a 1 , a 2 , a 3 a_1, a_2, a_3 a1,a2,a3 堆,每次合并两堆石子,若各有 x , y x, y x,y 个,合并的代价为 (x mod 3)(y mod 3) \text{(x mod 3)(y mod 3)} (x mod 3)(y mod 3) ,求合并所有石子的最小代价。
0 ≤ a i ≤ 1 0 9 , ∑ i = 1 3 a i > 0 0 \le a_i \le 10^9, \sum\limits_{i = 1}^{3} a_i \gt 0 0≤ai≤109,i=1∑3ai>0 。
若合并的其中一堆石子个数为 3 3 3 ,那么代价为 0 0 0 ,故可以忽略。剩下 a 1 a_1 a1 和 a 2 a_2 a2 ,显然如果同时有 a 1 > 0 a_1 \gt 0 a1>0 和 a 2 > 0 a_2 \gt 0 a2>0 ,优先对 1 1 1 和 2 2 2 进行合并,剩下只有一种石子,讨论模 3 3 3 的结果即可。
#include
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
ll a1, a2, a3; cin >> a1 >> a2 >> a3;
ll tmp = min(a1, a2);
ll ret = 2 * tmp;
a1 -= tmp, a2 -= tmp;
ret += a1 / 3 * 3 + (a1 % 3 == 2 ? 1 : 0);
ret += a2 / 3 * 6 + (a2 % 3 == 2 ? 4 : 0);
cout << ret << endl;
return 0;
}
有 n n n 个学生,每个学生论文的字数为 w i ∈ [ L i , R i ] w_i \in [L_i, R_i] wi∈[Li,Ri] ,若有 k i k_i ki 个学生 j ∈ [ 1 , n ] j \in [1, n] j∈[1,n] 满足 w j > w i w_j \gt w_i wj>wi ,则学生 i i i 的分数为 n − k i n - k_i n−ki 。最开始所有学生写了 R i R_i Ri 个字,求在每个学生分数不减小的情况下,最小的字数和是多少。
1 ≤ n ≤ 1 0 5 , 1 ≤ L i ≤ R i ≤ 1 0 9 1 \le n \le 10^5, 1 \le L_i \le R_i \le 10^9 1≤n≤105,1≤Li≤Ri≤109 。
按 R i R_i Ri 降序排序后,分数最低的一批学生显然可以同时尽可能减少字数,最小可以减到同分数段里 L i L_i Li 最大的那个。接下来考虑分数第二低的,除了 L i L_i Li 限制,还需要满足新字数仍大于前述那批学生,以此类推。
#include
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int n; cin >> n;
vector<pii> a(n);
for(int i = 0; i < n; ++i){
cin >> a[i].second >> a[i].first;
}
sort(a.begin(), a.end());
ll ret = 0; int lst = 0;
for(int l = 0, r; l < n; l = r){
r = l;
while(r < n && a[r].first == a[l].first) ++r;
int tmp = max(lst, a[r - 1].second);
ret += (r - l) * 1ll * tmp;
lst = tmp;
}
cout << ret << endl;
return 0;
}
待补。
待补。
给定两个正整数 X , Y X, Y X,Y ,每次操作可以选一个数 A A A ,满足 0 ≤ A < X 0 \le A \lt X 0≤A<X ,令 X = X ⊕ A X = X \oplus A X=X⊕A 。求在五次操作内将 X X X 变成 Y Y Y 。
1 ≤ Y < X ≤ 1 0 18 1 \le Y \lt X \le 10^{18} 1≤Y<X≤1018 。
讨论 X ⊕ Y X \oplus Y X⊕Y 的大小,如果 X ⊕ Y < X X \oplus Y \lt X X⊕Y<X ,那么一次操作就可以。否则 X ⊕ Y > X X \oplus Y \gt X X⊕Y>X ,先让 X = X ⊕ Y X = X \oplus Y X=X⊕Y ,再异或 X X X 即可。
#include
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
ll x, y; cin >> x >> y;
if((x ^ y) < x){
cout << "1" << endl;
cout << (x ^ y) << endl;
}
else{
cout << "2" << endl;
cout << y << " " << x << endl;
}
return 0;
}
给定一棵 n n n 个结点的有根树, 1 1 1 号结点为根结点。有 m m m 条路径 ( a i , b i ) (a_i, b_i) (ai,bi) ,其中 a i a_i ai 处在 b i b_i bi 到根的路径上。现在每次随机选择一个结点 u u u 并删除所有经过 u u u 的路径,问删除掉所有路径的期望次数。
1 ≤ n , m ≤ 300 , 1 ≤ a i ≤ b i ≤ n 1 \le n, m \le 300, 1 \le a_i \le b_i \le n 1≤n,m≤300,1≤ai≤bi≤n 。
记 w i w_i wi 为第 i i i 条路径被删除所需次数,则答案为 E [ max ( w i ) ] = E [ max ( S ) ] E[\max(w_i)] = E[\max(S)] E[max(wi)]=E[max(S)] 。最值反演得到答案为 ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 E [ min ( T ) ] \sum_{T \subseteq S} (-1)^{\vert T\vert - 1} E[\min(T)] ∑T⊆S(−1)∣T∣−1E[min(T)] 。对于给定的路径集合 T T T ,若覆盖了 k k k 个结点, E [ min ( T ) ] E[\min(T)] E[min(T)] 就是 n k \cfrac{n}{k} kn 。 d p ( i , j , k , 0 / 1 ) dp(i, j, k, 0/1) dp(i,j,k,0/1) 表示 i i i 子树内覆盖 j j j 个结点、往上覆盖了 k k k 个结点、集合大小为奇数或偶数时候的答案,转移时枚举子树逐一合并。只看前两维就是经典的树上背包复杂度,第三维由于是最值的形式,可以利用前缀和做到 O ( n ) O(n) O(n) ,总时间复杂度就是 O ( n 3 ) O(n^3) O(n3) 。注释代码部分是直接 O ( n 4 ) O(n^4) O(n4) 剪枝跑的,也能过。
#include
using namespace std;
typedef long long ll;
const int maxn = 3e2 + 5;
const int mod = 998244353;
vector<int> G[maxn], a[maxn];
int fa[maxn], siz[maxn], inv[maxn];
int dp[maxn][maxn][maxn][2], ans[maxn];
int n, m;
int getLen(int u, int v){
int ret = 1;
while(u != v) u = fa[u], ++ret;
return ret;
}
void dfs(int u){
dp[u][0][0][0] = 1, siz[u] = 1;
for(auto &v : a[u]){
int l = getLen(u, v);
memset(dp[0], 0, sizeof dp[0]);
for(int i = n; i >= 0; --i){
(dp[0][0][i][0] += dp[u][0][i][0]) %= mod;
(dp[0][0][i][1] += dp[u][0][i][1]) %= mod;
(dp[0][0][max(i, l)][0] += dp[u][0][i][1]) %= mod;
(dp[0][0][max(i, l)][1] += dp[u][0][i][0]) %= mod;
}
memcpy(dp[u], dp[0], sizeof dp[0]);
}
for(auto &v : G[u]){
dfs(v);
memset(dp[0], 0, sizeof dp[0]);
for(int i = siz[u] - 1; i >= 0; --i){
for(int j = siz[v] - 1; j >= 0; --j){
int sum[2] = {};
for(int k = 0; k <= n; ++k){
if(k) (sum[0] += dp[v][j][k][0]) %= mod;
if(k) (sum[1] += dp[v][j][k][1]) %= mod;
(dp[0][i + j][k][0] += dp[u][i][k][0] * 1ll * dp[v][j][0][0] % mod) %= mod;
(dp[0][i + j][k][0] += dp[u][i][k][1] * 1ll * dp[v][j][0][1] % mod) %= mod;
(dp[0][i + j][k][1] += dp[u][i][k][0] * 1ll * dp[v][j][0][1] % mod) %= mod;
(dp[0][i + j][k][1] += dp[u][i][k][1] * 1ll * dp[v][j][0][0] % mod) %= mod;
(dp[0][i + j + 1][k][0] += dp[u][i][k][0] * 1ll * sum[0] % mod) %= mod;
(dp[0][i + j + 1][k][0] += dp[u][i][k][1] * 1ll * sum[1] % mod) %= mod;
(dp[0][i + j + 1][k][1] += dp[u][i][k][0] * 1ll * sum[1] % mod) %= mod;
(dp[0][i + j + 1][k][1] += dp[u][i][k][1] * 1ll * sum[0] % mod) %= mod;
}
sum[0] = dp[u][i][0][0], sum[1] = dp[u][i][0][1];
for(int k = 1; k <= n; ++k){
(dp[0][i + j + 1][k - 1][0] += dp[v][j][k][0] * 1ll * sum[0] % mod) %= mod;
(dp[0][i + j + 1][k - 1][0] += dp[v][j][k][1] * 1ll * sum[1] % mod) %= mod;
(dp[0][i + j + 1][k - 1][1] += dp[v][j][k][0] * 1ll * sum[1] % mod) %= mod;
(dp[0][i + j + 1][k - 1][1] += dp[v][j][k][1] * 1ll * sum[0] % mod) %= mod;
(sum[0] += dp[u][i][k][0]) %= mod;
(sum[1] += dp[u][i][k][1]) %= mod;
}
// int p1 = n; while(p1 >= 0 && !dp[u][i][p1][0] && !dp[u][i][p1][1]) --p1;
// int p2 = n; while(p2 >= 0 && !dp[v][j][p2][0] && !dp[v][j][p2][1]) --p2;
// for(int k = p1; k >= 0; --k){
// for(int l = p2; l >= 0; --l){
// (dp[0][i + j + (l ? 1 : 0)][max(k, l - 1)][0] += dp[u][i][k][0] * 1ll * dp[v][j][l][0] % mod) %= mod;
// (dp[0][i + j + (l ? 1 : 0)][max(k, l - 1)][0] += dp[u][i][k][1] * 1ll * dp[v][j][l][1] % mod) %= mod;
// (dp[0][i + j + (l ? 1 : 0)][max(k, l - 1)][1] += dp[u][i][k][0] * 1ll * dp[v][j][l][1] % mod) %= mod;
// (dp[0][i + j + (l ? 1 : 0)][max(k, l - 1)][1] += dp[u][i][k][1] * 1ll * dp[v][j][l][0] % mod) %= mod;
// }
// }
}
}
memcpy(dp[u], dp[0], sizeof dp[0]);
siz[u] += siz[v];
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
inv[0] = inv[1] = 1;
for(int i = 2; i <= n; ++i){
inv[i] = inv[mod % i] * 1ll * (mod - mod / i) % mod;
}
for(int i = 2; i <= n; ++i){
cin >> fa[i];
G[fa[i]].emplace_back(i);
}
for(int i = 1; i <= m; ++i){
int x, y; cin >> x >> y;
a[y].emplace_back(x);
}
dfs(1);
for(int i = 0; i <= n; ++i){
for(int j = 0; j <= n; ++j){
if(i + j > n) break;
(ans[i + j] += dp[1][i][j][1]) %= mod;
(ans[i + j] -= dp[1][i][j][0]) %= mod;
}
}
ll ret = 0;
for(int i = 1; i <= n; ++i){
ll fi = n * 1ll * inv[i] % mod;
(ret += ans[i] * fi) %= mod;
// cout << i << " ?? " << ans[i] << endl;
}
ret = (ret + mod) % mod;
cout << ret << endl;
return 0;
}
待补。
输入一棵 n n n 个结点的树,构造序列 a i a_i ai ,使得当边 ( x , y ) (x, y) (x,y) 存在当且仅当 a x or a y = 2 60 − 1 a_x~\text{or}~a_y = 2^{60} - 1 ax or ay=260−1 。
1 ≤ n ≤ 100 , 0 ≤ a i < 2 60 1 \le n \le 100, 0 \le a_i \lt 2^{60} 1≤n≤100,0≤ai<260 。
树是二分图,假设左部点数较少,首先可以拿出两位,让左部点为 01 01 01 ,右部点为 10 10 10 ,那么可以保证左部与左部、右部与右部没有边。其次,对每个左部顶点 u u u 拿出一位,若右部点 v v v 与 u u u 没有边,则该位都为 0 0 0 ,否则 v v v 该位置为 1 1 1 ,注意左部除了 u u u 以外的点该位也要为 1 1 1 。需要的位数最多为 2 + ⌊ n 2 ⌋ 2 + \lfloor\cfrac{n}{2}\rfloor 2+⌊2n⌋ 。
#include
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 5;
vector<int> pi[2];
int G[maxn][maxn], col[maxn];
int n;
void dfs(int u, int f, int d){
pi[d].emplace_back(u), col[u] = d;
for(int v = 1; v <= n; ++v){
if(v == f) continue;
if(G[u][v]) dfs(v, u, d ^ 1);
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
for(int i = 1; i < n; ++i){
int u, v; cin >> u >> v;
G[u][v] = G[v][u] = 1;
}
dfs(1, 0, 0);
vector<ll> ans(n + 1, (1ll << 60) - 1);
if(pi[0].size() > pi[1].size()) swap(pi[0], pi[1]);
for(auto &v : pi[0]) ans[v] ^= 0b01;
for(auto &v : pi[1]) ans[v] ^= 0b10;
int d = 2;
for(auto &u : pi[0]){
ans[u] ^= 1ll << d;
for(int v = 1; v <= n; ++v){
if(!G[u][v] && col[v] != col[u]) ans[v] ^= 1ll << d;
}
++d;
}
// for(int i = 1; i <= n; ++i){
// cout << bitset<60>(ans[i]).to_string() << endl;
// }
for(int i = 1; i <= n; ++i){
cout << ans[i] << " \n"[i == n];
}
return 0;
}
给一个长度为 n n n 的序列 a i a_i ai ,定义 f ( a , S , K ) f(a, S, K) f(a,S,K) 为满足 b i = a i ⊕ S b_i = a_i \oplus S bi=ai⊕S 的 b i b_i bi 序列的第 K K K 小值。 Q Q Q 次询问,每次询问给定 L , R , K L, R, K L,R,K ,求 min S = L R f ( a , S , K ) \min_{S=L}^{R} f(a, S, K) minS=LRf(a,S,K) 。
1 ≤ n , Q ≤ 1 0 5 , 0 ≤ a i < 2 30 , 0 ≤ L ≤ R < 2 30 , 1 ≤ K ≤ n 1 \le n, Q \le 10^5, 0 \le a_i \lt 2^{30}, 0 \le L \le R \lt 2^{30}, 1 \le K \le n 1≤n,Q≤105,0≤ai<230,0≤L≤R<230,1≤K≤n 。
建立一棵字典树,将 a i a_i ai 插入。假设询问没有 L , R L, R L,R 限制,即求任意 S S S 下的第 K K K 小,可以预处理 k t h ( u , k ) kth(u, k) kth(u,k) 为 u u u 子树下 S S S 剩余位任意取值情况下的第 k k k 小值。转移时枚举当前 S S S 的取值,如果该位是 1 1 1 ,那么 c h ( u , 1 ) ch(u, 1) ch(u,1) 子树与 S S S 的异或值显然小于 c h ( u , 0 ) ch(u, 0) ch(u,0) 的,第 k k k 小来源根据子树大小讨论即可。
对于有 L , R L, R L,R 限制的,枚举高位限制可以得到在某一位后 S S S 的值没有限制情况下的答案,所有情况取最小。
#include
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = maxn * 30;
const int inf = 1 << 30;
vector<vector<int>> kth;
int a[maxn], nxt[maxm][2], siz[maxm];
int n, q, cnt;
const int lg = 29;
int add(){
return ++cnt;
}
void init(){
cnt = -1; add();
}
void insert(int x){
int p = 0; ++siz[p];
for(int i = lg; i >= 0; --i){
int t = (x >> i) & 1;
if(!nxt[p][t]) nxt[p][t] = add();
p = nxt[p][t], ++siz[p];
}
}
void dfs(int u, int d){
int v0 = nxt[u][0], v1 = nxt[u][1];
if(!v0 && !v1){
kth[u][1] = 0;
return;
}
if(v0) dfs(v0, d - 1);
if(v1) dfs(v1, d - 1);
for(int i = 1; i <= siz[u]; ++i){
kth[u][i] = inf;
if(siz[v0] >= i) kth[u][i] = min(kth[u][i], kth[v0][i]);
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[v1][i - siz[v0]]);
if(siz[v1] >= i) kth[u][i] = min(kth[u][i], kth[v1][i]);
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[v0][i - siz[v1]]);
}
}
void build(){
init();
for(int i = 1; i <= n; ++i){
insert(a[i]);
}
kth.resize(cnt + 1);
for(int i = 0; i <= cnt; ++i){
kth[i].resize(siz[i] + 1);
}
for(int i = 0; i <= n; ++i){
kth[0][i] = inf;
}
dfs(0, lg);
}
int query(int S, int k, int lim, int u = 0, int d = lg){
if(d < lim) return kth[u][k];
int t = (S >> d) & 1;
if(nxt[u][t] && siz[nxt[u][t]] >= k) return query(S, k, lim, nxt[u][t], d - 1);
else{
if(nxt[u][t]) k -= siz[nxt[u][t]];
return (1 << d) ^ query(S, k, lim, nxt[u][t ^ 1], d - 1);
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
build();
while(q--){
int l, r, k; cin >> l >> r >> k;
int p = lg;
while(p >= 0 && (l >> p & 1) == (r >> p & 1)) --p;
int ret = min(query(l, k, 0), query(r, k, 0));
for(int i = p - 1; i >= 0; --i){
if((l >> i) & 1) continue;
ret = min(ret, query(l ^ (1 << i), k, i));
}
for(int i = p - 1; i >= 0; --i){
if((~r >> i) & 1) continue;
ret = min(ret, query(r ^ (1 << i), k, i));
}
cout << ret << "\n";
}
return 0;
}
上面的代码是 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn) 的,可以优化到 O ( n l o g n ) O(nlogn) O(nlogn) :
#include
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e5 + 5;
const int maxm = maxn * 30;
const int inf = 1 << 30;
vector<vector<int>> kth;
int a[maxn], nxt[maxm][2], siz[maxm];
int n, q, cnt;
const int lg = 29;
int add(){
return ++cnt;
}
void init(){
cnt = -1; add();
}
void insert(int x){
int p = 0; ++siz[p];
for(int i = lg; i >= 0; --i){
int t = (x >> i) & 1;
if(!nxt[p][t]) nxt[p][t] = add();
p = nxt[p][t], ++siz[p];
}
}
void dfs(int u, int d){
int v0 = nxt[u][0], v1 = nxt[u][1];
if(!v0 && !v1){
kth[u][1] = 0;
return;
}
if(v0) dfs(v0, d - 1);
if(v1) dfs(v1, d - 1);
for(int i = 1; i <= siz[u]; ++i){
kth[u][i] = inf;
if(siz[v0] >= i) kth[u][i] = min(kth[u][i], kth[v0][i]);
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[v1][i - siz[v0]]);
if(siz[v1] >= i) kth[u][i] = min(kth[u][i], kth[v1][i]);
else kth[u][i] = min(kth[u][i], (1 << d) ^ kth[v0][i - siz[v1]]);
}
}
void build(){
init();
for(int i = 1; i <= n; ++i){
insert(a[i]);
}
kth.resize(cnt + 1);
for(int i = 0; i <= cnt; ++i){
kth[i].resize(siz[i] + 1);
}
for(int i = 0; i <= n; ++i){
kth[0][i] = inf;
}
dfs(0, lg);
}
int query(int x, int k, int lim, int flg){
auto walk = [](int &p, int &k, int t) -> int{
if(nxt[p][t] && siz[nxt[p][t]] >= k) {
p = nxt[p][t];
return 0;
}
else{
if(nxt[p][t]) k -= siz[nxt[p][t]];
p = nxt[p][t ^ 1];
return 1;
}
};
int p = 0, ret = inf, S = 0;
for(int i = lg; i >= 0; --i){
int t = (x >> i) & 1;
if(i < lim && t == flg){
int tp = p, tk = k, tS = S;
if(walk(tp, tk, t ^ 1)) tS ^= 1 << i;
ret = min(ret, tS ^ kth[tp][tk]);
}
if(walk(p, k, t)) S ^= 1 << i;
}
ret = min(ret, S ^ kth[p][k]);
return ret;
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
build();
while(q--){
int l, r, k; cin >> l >> r >> k;
int p = lg;
while(p >= 0 && (l >> p & 1) == (r >> p & 1)) --p;
int ret = min(query(l, k, p, 0), query(r, k, p, 1));
cout << ret << "\n";
}
return 0;
}
给定 L , m L, m L,m ,以及一个长度为 m m m 的 01 01 01 数组 a i a_i ai 。定义 f ( x ) f(x) f(x) 为 x x x 二进制 1 1 1 的个数。求有多少数 x ∈ [ 0 , L ] x \in [0, L] x∈[0,L] ,满足 ∀ i ∈ [ 0 , m − 1 ] , f ( x + i ) m o d 2 = a i \forall i \in [0, m - 1], f(x + i)~mod~ 2 = a_i ∀i∈[0,m−1],f(x+i) mod 2=ai 。
多组数据, 1 ≤ T ≤ 1000 , 1 ≤ m ≤ 100 , 0 ≤ L ≤ 1 0 18 , a i ∈ { 0 , 1 } 1 \le T \le 1000, 1 \le m \le 100, 0 \le L \le 10^{18}, a_i \in \{0, 1\} 1≤T≤1000,1≤m≤100,0≤L≤1018,ai∈{0,1} 。
由于 m m m 比较小,枚举 L L L 的低 7 7 7 位,那么 L + i L + i L+i 最多向高位进 1 1 1 ,考虑高位满足的数进行合并。定义 d p ( p o s , l i m , s u m , r e v ) dp(pos, lim, sum, rev) dp(pos,lim,sum,rev) 为当前枚举到 p o s pos pos 位、数位是否有限制、高位 1 1 1 个数的奇偶性、低位进 1 1 1 时高位 s u m sum sum 是否改变,数位 dp \text{dp} dp 到低 7 7 7 位时进行枚举。
#include
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
ll dp[64][2][2][2], L;
int a[105], b[64], m;
int cal(int lim, int sum, int rev){
int up = lim ? L % 128 : 127, ret = 0;
for(int i = 0; i <= up; ++i){
int flg = 1;
for(int j = 0; j < m && flg; ++j){
if(i + j < 128) flg &= __builtin_parity(i + j) ^ sum == a[j];
else flg &= __builtin_parity(i + j - 128) ^ sum ^ rev == a[j];
}
ret += flg;
}
// cout << lim << " " << sum << " " << rev << " " << ret << endl;
return ret;
}
ll dfs(int pos, int lim, int sum, int rev){
ll &ret = dp[pos][lim][sum][rev];
if(~ret) return ret;
if(pos <= 6) return ret = cal(lim, sum, rev);
ret = 0;
int up = lim ? b[pos] : 1;
for(int i = 0; i <= up; ++i){
ret += dfs(pos - 1, lim && i == up, sum ^ i, i ? rev ^ 1 : 1);
}
return ret;
}
ll solve(){
memset(dp, -1, sizeof dp);
int len = 0;
for(ll x = L; x; x >>= 1) b[len++] = x & 1;
if(L == 0) b[len++] = 0;
return dfs(len - 1, 1, 0, 1);
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int T; cin >> T;
while(T--){
cin >> m >> L;
for(int i = 0; i < m; ++i){
cin >> a[i];
}
cout << solve() << "\n";
}
return 0;
}
有 n n n 个煎饼和 k k k 个锅,每个锅只能在一分钟时间煎一面,问煎完的最少时间。
1 ≤ n , k ≤ 100 1 \le n, k \le 100 1≤n,k≤100 。
当 k ≥ n k \ge n k≥n 答案为 2 2 2 。否则按编号顺序先煎完正面、再煎反面,由于 k < n k \lt n k<n ,不会冲突,答案就是 ⌈ 2 n k ⌉ \lceil\cfrac{2n}{k}\rceil ⌈k2n⌉ 。
#include
using namespace std;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
int n, k; cin >> n >> k;
cout << max(2, (2 * n + k - 1) / k) << endl;
return 0;
}