oj: 牛客
A 回文括号序列计数
oj: 牛客
题意
询问长度为 n n n 的括号序列的个数。
括号序列定义:
- 空串是括号序列。
- 两个括号序列 P P P 和 Q Q Q 的拼接是括号序列。
- 如果 P P P 是括号序列,‘(’+ P P P +')'是括号序列。
题解
脑筋急转弯题。
一个长度不为0的括号序列的两端字符必定不同,所以左右反转后一定不是括号序列,更不可能与之前相同。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int main() {
int T = read();
_for(i, T) {
int n = read();
if(n == 0) printf("1\n");
else printf("0\n");
}
return 0;
}
C 末三位
oj: 牛客
题意
求出 5 n 5^n 5n 的末三位数字。
题解
快速幂,中间对1000取模即可。
注意输出时要有前导0。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int mod = 1000;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
LL quickPow(LL x, LL n, LL mod) {
LL ans = 1;
while(n) {
if(n & 1) ans *= x, ans %= mod;
x *= x, x %= mod;
n >>= 1;
}
return ans;
}
int main() {
LL n;
while(~scanf("%lld", &n)) {
LL ans = quickPow(5, n, mod);
int len = 0;
for(LL x = ans; x; ++len) x /= 10;
_for(i, 3 - len) printf("0");
printf("%lld\n", ans);
}
return 0;
}
D 划数
oj: 牛客
题意
给定一个数组。每回合取出其中两个数,然后放回这两个数的和对 11 11 11 取余的值,直到数组还剩两个数。
现在已知数组还剩 c n t ( c n t ≥ 11 ) cnt(cnt\ge 11) cnt(cnt≥11)和一个未知数,求出那个未知数。
题解
当 n n n 为 2 2 2 时,显然答案就是另一个数。
否则,因为 c n t ≥ 11 cnt\ge 11 cnt≥11,所以它不可能是取出又放回的数,那么那个未知数就一定是取出又放回的数。
而且拿出又放回是不影响数组总和对 11 11 11 取余的值的,所以直接拿数组总和减去 c n t cnt cnt,然后对 11 11 11 取模即可。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
LL n, cnt;
void sol() {
LL sum = 0;
_for(i, n) sum += read();
if(n == 2) printf("%lld\n", sum - cnt);
else printf("%lld\n", (sum - cnt) % 11);
}
int main() {
while (~scanf("%lld%lld", &n, &cnt)) {
sol();
}
return 0;
}
E 网格
oj: 牛客
题意
有一个 n n n 行 m m m 列的网格,第 i i i 行 j j j 列上有数字 a i , j a_{i,j} ai,j。每个位置需要从上下左右四个方向中选择互相垂直的两个。
定义 w ( x ) = x + p o p c n t ( x ) w(x)=x+popcnt(x) w(x)=x+popcnt(x) ,其中 p o p c n t ( x ) popcnt(x) popcnt(x) 表示 x x x 的二进制位中 1 1 1 的位的数量。
如果两个相邻的位置 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1,y1),(x2,y2) 互相位于对方选择的某个方向上,则对答案由 w ( a x 1 , y 1 x o r a x 2 , y 2 ) w(a_{x_1,y_1}\ xor\ a_{x_2,y_2}) w(ax1,y1 xor ax2,y2) 的贡献,其中 x o r xor xor 表示二进制中的按位异或。
求出最大值。
题解
参考自牛客959548301号的题解。
题目就相当于,有一个网格图,要选出若干条边,要求每个点不能同时有向(上&下)或(左&右)的边,求最大边权和。
首先假设 n = 1 n=1 n=1,限制就变成了不能选相邻两条边,求最大代价,发现只能dp。
发现行列是独立的,互不影响,所以对行和列分别计算。
又发现行与行之间互相又是独立的,所以对每一行分别计算。(列同理)
限制条件就是同一行中不能选相邻两条边(不然有一个点同时选了左右)
d p [ i ] [ 0 / 1 ] dp[i][0/1] dp[i][0/1] 表示前 i i i 条边,上一个选没选的最大价值。
转移就枚举这个点选不选,上个点选不选分别转移。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 1005;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n, m;
LL a[maxn][maxn];
LL dp[maxn][2];
LL getval(LL a, LL b) {
LL tem = a ^ b, num = 0;
for(LL x = tem; x; x >>= 1) if(x & 1) ++num;
return tem + num;
}
void sol() {
_rep(i, 1, n) _rep(j, 1, m) a[i][j] = read();
LL ans = 0;
_rep(i, 1, n) {
_rep(j, 1, m) dp[j][0] = dp[j][1] = 0;
dp[2][1] = getval(a[i][1], a[i][2]);
_rep(j, 3, m) {
dp[j][0] = max(dp[j - 1][0], dp[j - 1][1]);
dp[j][1] = dp[j - 1][0] + getval(a[i][j], a[i][j - 1]);
}
ans += max(dp[m][0], dp[m][1]);
}
_rep(i, 1, m) {
_rep(j, 1, n) dp[j][0] = dp[j][1] = 0;
dp[2][1] = getval(a[1][i], a[2][i]);
_rep(j, 3, n) {
dp[j][0] = max(dp[j - 1][0], dp[j - 1][1]);
dp[j][1] = dp[j - 1][0] + getval(a[j][i], a[j - 1][i]);
}
ans += max(dp[n][0], dp[n][1]);
}
printf("%lld\n", ans);
}
int main() {
n = read(), m = read();
sol();
return 0;
}
F 组合数问题
oj: 牛客
题意
求出 ( n 0 ) + ( n 4 ) + ( n 8 ) + . . . + ( n n ) \binom{n}{0}+\binom{n}{4}+\binom{n}{8}+...+\binom{n}{n} (0n)+(4n)+(8n)+...+(nn) 的值,结果对 998244353 998244353 998244353 取余。
题解
打表之后OEIS出一个公式:
f ( n ) = 4 f ( n − 1 ) − 6 f ( n − 2 ) + 4 f ( n − 3 ) f(n)=4f(n-1)-6f(n-2)+4f(n-3) f(n)=4f(n−1)−6f(n−2)+4f(n−3)
之后利用矩阵快速幂求解。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (LL i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (LL i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const LL mod = 998244353;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct mat {
LL a[3][3];
mat(){
_for(i, 3) _for(j, 3) a[i][j] = 0;
}
};
mat mat_mul(mat x, mat y) {
mat res; //用来表示得到的新的矩阵;
memset(res.a, 0, sizeof(res.a));
for (LL i = 0; i < 3; i++)
for (LL j = 0; j < 3; j++)
for (LL k = 0; k < 3; k++) {
res.a[i][j] += x.a[i][k] * y.a[k][j];
res.a[i][j] %= mod;
}
return res;
}
LL pow(LL n) {
mat c, res;
c.a[0][0] = 4;
c.a[0][1] = -6;
c.a[0][2] = 4;
c.a[1][0] = 1;
c.a[2][1] = 1;
for (LL i = 0; i < 3; i++) res.a[i][i] = 1;
while (n) {
if (n & 1) res = mat_mul(res, c);
c = mat_mul(c, c);
n = n >> 1;
}
LL ans = 0;
_for(i, 3) ans += res.a[0][i], ans %= mod;
return (ans % mod + mod) % mod;
}
LL sol(LL n) {
if(n <= 3) return 1;
return pow(n - 3);
}
int main() {
LL n;
while(~scanf("%lld", &n)) {
printf("%lld\n", sol(n));
}
return 0;
}
G 机器人
oj: 牛客
题意
有 n n n 个机器人,每个机器人会读入一个 x x x ,并返回 a x + b ax+b ax+b 。
给定一个数 x x x,通过对机器人重新排序求出返回的最大值。
题解
状压 d p dp dp 求解即可。
注意结果会爆 l o n g l o n g long long longlong,改用 i n t 128 int128 int128。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef __int128 LL;
const int maxn = 25;
inline LL read() {
LL x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
inline void write(LL x) {
if (x < 0) { putchar('-'); x = -x; }
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
LL dp[1 << 21];
LL n, x;
LL a[maxn], b[maxn];
void sol() {
_for(i, n) a[i] = read(), b[i] = read();
_for(i, n) dp[1 << i] = a[i] * x + b[i];
for(int s = 1; s < (1 << n); ++s) {
_for(j, n) {
if((s | (1 << j)) > s) {
dp[s | (1 << j)] = max(dp[s | (1 << j)], dp[s] * a[j] + b[j]);
}
}
}
write(dp[(1 << n) - 1]);
printf("\n");
}
int main() {
n = read(), x = read();
sol();
return 0;
}
H 动态最小生成树
oj: 牛客
题意
有一张 n n n 个点 m m m 条边的图,每条边连接点 u i , v i u_i,v_i ui,vi,边权为 w i w_i wi。他想进行 q q q 次操作,有如下两种类型:
- 修改第 x x x 条边为连接点 y , z y,z y,z ,边权为 t t t;
- 查询只用编号在 [ l , r ] [l,r] [l,r] 范围内的边,得到的最小生成树权值是多少。
题解
这题数据较弱,暴力也可过。
代码
#include <bits/stdc++.h>
#define m_p make_pair
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 205;
const int maxm = 30005;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct poi {
int u, v, w;
poi(){}
poi(int u, int v, int w):u(u), v(v), w(w) {}
}a[maxm];
int n, m, q;
template <typename T>
struct Kruskal {
int f[maxn];
vector< pair<T, pair<int, int> > > arr;
void init() {
arr.clear();
_for(i, n + 1) f[i] = i;
}
int find(int a) { return a == f[a] ? a : f[a] = find(f[a]); }
void update(int a, int b) { f[find(a)] = find(b); }
void addedge(int u, int v, T w) { arr.push_back(m_p(w, m_p(u, v))); }
T doit() {
T ans = 0;
sort(arr.begin(), arr.end());
_for(i, arr.size()) {
if (find(arr[i].second.first) != find(arr[i].second.second)) {
ans += arr[i].first;
update(arr[i].second.first, arr[i].second.second);
}
}
return ans;
}
int che() {
for(int i = 2; i <= n; ++i) if(find(i) != find(1)) return 0;
return 1;
}
};
Kruskal<LL> k;
void sol() {
_rep(i, 1, m) {
int u = read(), v = read(), w = read();
a[i] = poi(u, v, w);
}
_for(i, q) {
int op = read();
if(op == 1) {
int x = read(), y = read(), z = read(), t = read();
a[x] = poi(y, z, t);
}
else {
int l = read(), r = read();
k.init();
_rep(i, l, r) k.addedge(a[i].u, a[i].v, a[i].w);
LL ans = k.doit();
if(!k.che()) printf("Impossible\n");
else printf("%lld\n", ans);
}
}
}
int main() {
n = read(), m = read(), q = read();
sol();
return 0;
}
H 动态最小生成树
oj: 牛客
题意
给定起点和终点,检索是否存在路径,若有则输出最短路径长度。
题解
b f s bfs bfs 即可。
代码
#include <bits/stdc++.h>
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 105;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
struct poi {
int x, y, dep;
poi() {}
poi(int x, int y, int dep):x(x), y(y), dep(dep) {}
};
int ans = -1;
char s[maxn][maxn];
int n, m;
int sx, sy, tx, ty;
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
void bfs() {
queue< poi > q;
q.push(poi(sx, sy, 0));
s[sx][sy] = '#';
while(q.size()) {
poi t = q.front(); q.pop();
if(t.x == tx && t.y == ty) {
ans = t.dep * 100;
return;
}
_for(k, 4) {
int xx = t.x + dir[k][0], yy = t.y + dir[k][1];
if(s[xx][yy] == '.') {
s[xx][yy] = '#';
q.push(poi(xx, yy, t.dep + 1));
}
}
}
}
void init() {
ans = -1;
}
void sol() {
init();
sx = read(), sy = read(), tx = read(), ty = read();
_rep(i, 1, n) scanf("%s", s[i] + 1);
bfs();
printf("%d\n", ans);
}
int main() {
n = read(), m = read();
sol();
return 0;
}
J 天空之城
oj: 牛客
题意
有 n n n 个城市和 m m m 条边,询问能否遍历所有城市。每个城市可以多次经过,且重复经过时不计算额外时间。
题解
显然直接求最小生成树,然后判断是否覆盖全部城市即可。
代码
#include <bits/stdc++.h>
#define m_p make_pair
#define _for(i, a) for (int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for (int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;
inline LL read() {
LL x(0), f(1); char ch(getchar());
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int cnt = 0;
int n, m;
string s;
map<string, int> mp;
void init() {
mp.clear();
cnt = 0;
}
template <typename T>
struct Kruskal {
int f[maxn];
vector< pair<T, pair<int, int> > > arr;
void init() {
arr.clear();
}
int find(int a) { return a == f[a] ? a : f[a] = find(f[a]); }
void update(int a, int b) { f[find(a)] = find(b); }
void addedge(int u, int v, T w) { arr.push_back(m_p(w, m_p(u, v))); }
T doit() {
T ans = 0;
sort(arr.begin(), arr.end());
_for(i, arr.size()) {
if (find(arr[i].second.first) != find(arr[i].second.second)) {
ans += arr[i].first;
update(arr[i].second.first, arr[i].second.second);
}
}
return ans;
}
};
Kruskal<LL> k;
void sol() {
init();
k.init();
cin >> s;
_for(i, m) {
cin >> s;
if(!mp.count(s)) mp[s] = cnt++;
int u = mp[s];
cin >> s;
if(!mp.count(s)) mp[s] = cnt++;
int v = mp[s];
LL val;
cin >> val;
k.addedge(u, v, val);
}
_for(i, n) k.f[i] = i;
LL ans = k.doit();
for(int i = 1; i < n; ++i) {
if(k.find(i) != k.find(0)) {
cout << "No!\n";
return;
}
}
cout << ans << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
while(cin >> n >> m) {
sol();
}
return 0;
}