比赛链接:
https://vjudge.net/contest/505856
A - String
exkmp,kmp树
B - Dragon slayer
题意:
\(n * m\) 的地图中,有 \(k\) 堵墙,施展一次魔法可以消除一堵墙,问从起点能够到达终点,最少消除多少堵墙。
思路:
数据范围小,爆搜或者状压(还没补)。
爆搜思路:
因为是在格子中走,所以将地图扩大两倍,在点上走,然后二进制枚举所有状态,将没有被破坏的墙添加到地图中,取一个最小的值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL dx[] = {-1, 0, 1, 0}, dy[] = {0, -1, 0, 1};
void solve(){
LL n, m, k;
cin >> n >> m >> k;
n = n * 2, m = m * 2;
LL sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
sx = sx * 2 + 1, sy = sy * 2 + 1;
tx = tx * 2 + 1, ty = ty * 2 + 1;
vector <LL> x(k), y(k), X(k), Y(k);
for (int i = 0; i < k; i ++ ){
cin >> x[i] >> y[i] >> X[i] >> Y[i];
x[i] *= 2, y[i] *= 2;
X[i] *= 2, Y[i] *= 2;
}
LL ans = 20;
bool ok = false;
vector < vector<LL> > v(n + 1, vector<LL>(m + 1, 0));
vector < vector<LL> > a(n + 1, vector<LL>(m + 1, -1));
function<void(LL, LL)> dfs = [&] (LL x, LL y){
if (x == tx && y == ty){
ok = true;
return;
}
v[x][y] = 1;
for (int i = 0; i < 4; i ++ ){
LL nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || ny < 0 || nx > n || ny > m) continue;
if (v[nx][ny]) continue;
if (~a[nx][ny]) continue;
dfs(nx, ny);
}
};
for (int s = 0; s < (1 << k); s ++ ){
LL t = s, cnt = 0;
for (int i = 0; i < k; i ++ , t >>= 1 ){
LL tt = t & 1;
if (!tt){
for (int p = x[i]; p <= X[i]; p ++ )
for (int q = y[i]; q <= Y[i]; q ++ )
a[p][q] = i;
}
cnt = cnt + tt;
}
if (cnt >= ans){ //破坏的墙的数量大于已知答案,所以没必要进行搜索了
for (int i = 0; i <= n; i ++ )
for (int j = 0; j <= m; j ++ )
v[i][j] = 0, a[i][j] = -1;
continue;
}
ok = false;
dfs(sx, sy);
if (ok){
ans = min(ans, cnt);
}
for (int i = 0; i <= n; i ++ )
for (int j = 0; j <= m; j ++ )
v[i][j] = 0, a[i][j] = -1;
}
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
C - Backpack
题意:
有 \(n\) 个物品和一个容量为 \(m\) 的背包,第 \(i\) 件物品的体积为 \(v\),价值为 \(w\),物品的价值之和为它们的价值的异或和,问是否存在一种方案使得背包装满,若有,输出装满时的最大价值。
思路:
以体积作为第一维,异或值作为第二维,不好求最大的异或值,所以反一下。
设 \(dp[i][j]\) 为异或值为 i 时,体积为 \(j\) 的方案。
得到转移方程 \(dp[i][j] = dp[i][j]\) | \(dp[i\) ^ \(w][j - v]\)。
体积那一维可以通过 \(bitset\) 去优化。
通过滚动数组对空间进行了优化。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL n, m;
cin >> n >> m;
bitset <1030> f[1030], g[1030];
f[0][0] = 1;
for (int i = 0; i < n; i ++ ){
LL v, w;
cin >> v >> w;
for (int j = 0; j < 1024; j ++ ) //体积转移
g[j] = f[j] << v;
for (int j = 0; j < 1024; j ++ ) //异或值转移
f[j] |= g[j ^ w];
}
for (int j = 1023; j >= 0; j -- ){
if (f[j][m]){
cout << j << "\n";
return;
}
}
cout << "-1\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
I - Laser
模拟
K - Random
题意:
有 \(n\) 个在 [0, 1] 范围的随机数。对它们进行 \(m\) 次操作,每次有 \(\frac{1}{2}\) 的几率删除其中的最大值,有 \(\frac{1}{2}\) 的几率删除其中的最小值。问最后剩余的值之和为多少。
思路:
特殊情况,全是 0.5,删除了 \(m\) 个,那剩下的就是 \(\frac{n - m}{2}\)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 1e9 + 7;
LL qp(LL a, LL k, LL p){
LL ans = 1;
while (k){
if (k & 1) ans = ans * a % p;
k >>= 1;
a = a * a % p;
}
return ans;
}
LL inv(LL x){
return qp(x, mod - 2, mod);
}
void solve(){
LL n, m;
cin >> n >> m;
cout << (n - m) * inv(2) % mod << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
L - Alice and Bob
题意:
黑板上有 \(n\) 种数,第 \(i\) 种数有 \(a_i\) 个,每回合 \(Alice\) 会将数分成两个集合,然后 \(Bob\) 删除其中一个集合中的数,并让另一个集合中的数全部减去 1,当有一个数字为 0 时 \(Alice\) 获胜,若数字无法再分,那么 \(Bob\) 获胜。
思路:
从 \(Alice\) 的必胜态出发,当它有 1 个 0 的时候获胜,有两个 1 的时候也可以获胜,四个 2 的时候获胜...以此类推下去。
所以可以反向操作,每次让第 \(i\) 个数加上 \(i + 1\) 的数的一半的量,如果可以得到 0,说明 \(Alice\) 获胜。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL n;
cin >> n;
vector <LL> a(n + 1);
for (int i = 0; i <= n; i ++ )
cin >> a[i];
for (int i = n - 1; i >= 0; i -- )
a[i] += a[i + 1] / 2;
cout << (a[0] ? "Alice" : "Bob") << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}