2025牛客寒假算法基础集训营3 题解
Preface
又来了又来了。这场开始去忙别的事情了,从大概2个小时半才开始打的,只能说很是可惜,这场最难的银牌题并不是太难,错失了上大分的机会。
发现寒假训练营没怎么出dp和数学,这样的题比较少,但是这种类型的应该在ICPC中会比较常见才对,只能说不太擅长的都没怎么出现。
我会在代码一些有必要的地方加上注释,签到题可能一般就不会写了.
以下是代码火车头:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
template<typename T>
void cc(const vector<T> &tem) {
for (const auto &x: tem) cout << x << ' ';
cout << endl;
}
template<typename T>
void cc(const T &a) { cout << a << endl; }
template<typename T1, typename T2>
void cc(const T1 &a, const T2 &b) { cout << a << ' ' << b << endl; }
template<typename T1, typename T2, typename T3>
void cc(const T1 &a, const T2 &b, const T3 &c) { cout << a << ' ' << b << ' ' << c << endl; }
void cc(const string &s) { cout << s << endl; }
void fileRead() {
#ifdef LOCALL
freopen("D:\\AADVISE\\Clioncode\\untitled2\\in.txt", "r", stdin);
freopen("D:\\AADVISE\\Clioncode\\untitled2\\out.txt", "w", stdout);
#endif
}
void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }
inline int max(int a, int b) {
if (a < b) return b;
return a;
}
inline double max(double a, double b) {
if (a < b) return b;
return a;
}
inline int min(int a, int b) {
if (a < b) return a;
return b;
}
inline double min(double a, double b) {
if (a < b) return a;
return b;
}
void cmax(int &a, const int &b) { if (b > a) a = b; }
void cmin(int &a, const int &b) { if (b < a) a = b; }
void cmin(double &a, const double &b) { if (b < a) a = b; }
void cmax(double &a, const double &b) { if (b > a) a = b; }
using PII = pair<int, int>;
using i128 = __int128;
using vec_int = std::vector<int>;
using vec_char = std::vector<char>;
using vec_double = std::vector<double>;
using vec_int2 = std::vector<std::vector<int> >;
using que_int = std::queue<int>;
Problem A. 智乃的博弈游戏
由打表得,直接通过奇数偶数来判断就好了,具体为啥请移步官方题解。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n;
if (n % 2) {
cc("Yes");
}
else {
cc("No");
}
}
return 0;
}
Problem C. 智乃的Notepad(Easy version)
这里直接和D题一起讲了。
是谁时间复杂度搞错了一直卡这题我不说。
首先本题是可以转化成字典树上的,一个区间里的贡献就是将区间的字符串放到字典树上,然后所有字典树的节点的个数*2(不包括根节点那个空节点)然后再减去最大深度就好了。这个贡献我们在纸上画画就可以得出来了。大体思路就是只有最长的那个链我们可以不走回头路,而其他的点我们都需要走一次回头路,那么就是所有的点数去乘2,然后减去最长的链。
NND赛时时间复杂度算错了,以为可以直接字典树维护用莫队,不会T,但是实际上忘了考虑每一次添加和删除字符串在树上的复杂度是O(n),我说怎么这么怪。忽略了这个,还想着直接莫队暴力跑就好了,结果一直没过去。还有就是字典树太久没写一直WA,真丢人。
首先ez版本的直接搞一颗字典树就好了,求出来节点总数和最大深度,就可以直接算出来答案。
ok现在来说一下正解,那就是刚才说的那个贡献,我们后者减去最大深度,就是减去区间内的最大的长度值,这个很ez。那么维护区间的所有节点个数,我们可以用扫描线的思想,for循环从左往右,然后每次去处理当前点为区间右端点的询问。
然后思路就可以像官方的那样,颜色赋值。这里插一张官方的图片(非常清晰)
利用字典树和线段树,线段树是用来处理区间操作的,根据颜色来开一个线段树,然后我们每一次插入一个字符串,在字典树上进行操作,如果当前点有覆盖过颜色,那么在线段树上把对应的颜色的值-1,把当前的颜色的值+1。当然这样做常数会有点大,我不知道会不会卡,应该不会,常数小点就可以这样:我们可以把当前的颜色加的值先统计起来,最后插入完了再直接在线段树上加上。其实应该没大讲。
然后关于求最大深度的,用线段树或者ST表都可以。
代码巨长,抄了两个线段树板子和一个字典树板子。
//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
class SEG {
#define xl x+x
#define xr x+x+1
//TODO 节点维护信息、apply函数、up函数
struct info {
int sum = 0;
void apply(int k) {
sum += k;
}
friend info operator+(const info &q1, const info &q2) {
info q;
q.sum = q1.sum + q2.sum;
return q;
}
};
int L, R;
info F[unsigned(N * 2.7)];
void init(int x, int l, int r) {
if (l == r) {
F[x] = info();
return;
}
int mid = l + r >> 1;
init(xl, l, mid), init(xr, mid + 1, r);
F[x] = F[xl] + F[xr];
}
void add(int x, int l, int r, int l1, int r1, int k) {
if (l1 > r1) return;
if (l1 <= l and r <= r1) {
F[x].apply(k);
return;
}
int mid = l + r >> 1;
if (r1 <= mid) add(xl, l, mid, l1, r1, k);
else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
F[x] = F[xl] + F[xr];
}
info qry(int x, int l, int r, int l1, int r1) {
if (l1 > r1) return info();
if (l1 <= l and r <= r1) return F[x];
int mid = l + r >> 1;
if (r1 <= mid) return qry(xl, l, mid, l1, r1);
else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
}
#undef xl
#undef xr
public:
void clear(int l, int r) {
L = l, R = r;
init(1, l, r);
}
void add(int l, int r, int k) { add(1, L, R, l, r, k); }
info qry(int l, int r) { return qry(1, L, R, l, r); }
};
class SEG1 {
#define xl x+x
#define xr x+x+1
//TODO 节点维护信息、apply函数、up函数
struct info {
// int sum = 0;
int mmax = 0;
void apply(int k) {
mmax += k;
}
friend info operator+(const info &q1, const info &q2) {
info q;
// q.sum = q1.sum + q2.sum;
q.mmax = max(q1.mmax, q2.mmax);
return q;
}
};
int L, R;
info F[unsigned(N * 2.7)];
void init(int x, int l, int r) {
if (l == r) {
F[x] = info();
return;
}
int mid = l + r >> 1;
init(xl, l, mid), init(xr, mid + 1, r);
F[x] = F[xl] + F[xr];
}
void add(int x, int l, int r, int l1, int r1, int k) {
if (l1 > r1) return;
if (l1 <= l and r <= r1) {
F[x].apply(k);
return;
}
int mid = l + r >> 1;
if (r1 <= mid) add(xl, l, mid, l1, r1, k);
else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
F[x] = F[xl] + F[xr];
}
info qry(int x, int l, int r, int l1, int r1) {
if (l1 > r1) return info();
if (l1 <= l and r <= r1) return F[x];
int mid = l + r >> 1;
if (r1 <= mid) return qry(xl, l, mid, l1, r1);
else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
}
#undef xl
#undef xr
public:
void clear(int l, int r) {
L = l, R = r;
init(1, l, r);
}
void add(int l, int r, int k) { add(1, L, R, l, r, k); }
info qry(int l, int r) { return qry(1, L, R, l, r); }
};
SEG seg0;
SEG1 seg1;
struct qq {
int l;
int id;
};
vec<qq> Q[M];
int ksum = 0;
int co[N];
namespace Z {
static constexpr int MAXN = (N * 21);
#define y son[x][i]
struct info {
};
struct node {
info q = info();
int val = 0;
void apply(int c) {
if (val != 0) {
co[val] -= 1;
seg0.add(val, val, -1);
}
val = c;
co[val] += 1;
}
void apply_end(int c) {
}
} F[MAXN];
int son[MAXN][27], tot = 0, rt = 0;
int s_len;
void up(int x) {
if (F[x].val == 0) return;
F[x].q = info();
rep(i, 0, 25) {
if (F[y].val == 0) continue;
}
}
#undef y
int newNode() {
F[++tot] = node();
rep(i, 0, 25) son[tot][i] = 0;
return tot;
}
void add(int &x, int dep, const string &s, int c) {
if (!x) x = newNode();
F[x].apply(c);
if (dep >= s_len) {
F[x].apply_end(c);
return;
}
int bit = s[dep] - 'a';
add(son[x][bit], dep + 1, s, c);
up(x);
return;
}
void clear() {
tot = rt = 0;
F[0] = node();
}
void add(const string &s, int c) {
s_len = s.size();
add(rt, 0, s, c);
}
}
string A[N];
int ans[N];
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> A[i];
}
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
Q[b].push_back({a, i});
}
Z::clear();
// rep(i, 0, 1)
seg0.clear(1, n);
seg1.clear(1, n);
rep(i, 1, n) {
int r = i;
Z::add(A[i], i);
seg0.add(i, i, co[i]);
seg1.add(i, i, A[i].size());
for (auto [l,id]: Q[i]) {
int sum = seg0.qry(l, r).sum;
int mmax = seg1.qry(l, r).mmax;
ans[id] = (sum - 1) * 2 - mmax;
// cc(id, sum);
}
}
rep(i, 1, m) {
cc(ans[i]);
}
}
return 0;
}
/*
*/
Problem D. 智乃的Notepad(Hard version)
这里在C题讲过了,就不过多赘述了。
Problem E. 智乃的小球
非常典,球相撞直接想成会穿过去就好了。
然后这种那么显然具有单调性的问题直接无脑二分家人们。当然首先我们显然以坐标sort排序一下。
然后非常暴力的求解,每一次二分mid是时间,去看向右走的球在mid时间会碰到多远的向左的球的位置。
我们直接向右是0,向左是1。然后我们假设
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
class SEG {
#define xl x+x
#define xr x+x+1
//TODO 节点维护信息、apply函数、up函数
struct info {
int sum = 0;
void apply(int k) {
sum += k;
}
friend info operator+(const info &q1, const info &q2) {
info q;
q.sum = q1.sum + q2.sum;
return q;
}
};
int L, R;
info F[unsigned(N * 2.7)];
void init(int x, int l, int r) {
if (l == r) {
F[x] = info();
return;
}
int mid = l + r >> 1;
init(xl, l, mid), init(xr, mid + 1, r);
F[x] = F[xl] + F[xr];
}
void add(int x, int l, int r, int l1, int r1, int k) {
if (l1 > r1) return;
if (l1 <= l and r <= r1) {
F[x].apply(k);
return;
}
int mid = l + r >> 1;
if (r1 <= mid) add(xl, l, mid, l1, r1, k);
else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
F[x] = F[xl] + F[xr];
}
info qry(int x, int l, int r, int l1, int r1) {
if (l1 > r1) return info();
if (l1 <= l and r <= r1) return F[x];
int mid = l + r >> 1;
if (r1 <= mid) return qry(xl, l, mid, l1, r1);
else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
}
#undef xl
#undef xr
public:
void clear(int l, int r) {
L = l, R = r;
init(1, l, r);
}
void add(int l, int r, int k) { add(1, L, R, l, r, k); }
info qry(int l, int r) { return qry(1, L, R, l, r); }
};
SEG seg;
struct node {
int pos;
int val;
};
node A[N];
vec<int> id;
//--------------------------------------------------------------------------------
int get(int rr) {
int l = 0, r = n + 1;
while (l + 1 != r) {
int mid = l + r >> 1;
if (A[mid].pos <= rr) l = mid;
else r = mid;
}
return l;
}
bool check(double mid) {
int num = 0;
int len = id.size();
rep(i, 0, len-1) {
int pos = id[i];
int r = floor(A[pos].pos + mid * 2);
int rpos = get(r);
num += seg.qry(pos, rpos).sum;
if (num >= m) return 1;
}
return 0;
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
rep(i, 1, n) {
int a, b;
cin >> a >> b;
A[i] = {a, b};
}
sort(A + 1, A + n + 1, [&](node &q1, node &q2) {
return q1.pos < q2.pos;
});
seg.clear(1, n);
rep(i, 1, n) {
int fl = 0;
if (A[i].val == -1) fl = 1;
else fl = 0;
seg.add(i, i, fl);
if (fl == 0) id.push_back(i);
}
double l = 0, r = 1e9 + 10;
while (fabs(r - l) > 1e-7) {
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
if (r > 1e9) {
cc("No");
continue;
}
cc("Yes");
cout << fixed << setprecision(8) << r << endl;
}
return 0;
}
/*
*/
Problem F. 智乃的捉迷藏
直接大力盲猜a+b+c如果小于n或者大于n+n就不行,否则就可以。不要问为什么。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
int A[N];
int f(char x) {
return x - 'a';
}
signed main() {
fileRead();
kuaidu();
T = 1;
cin >> T;
while (T--) {
int a, b, c;
cin >> n >> a >> b >> c;
if (a + b + c < n or a + b + c > 2 * n) {
cc("No");
}
else {
cc("Yes");
}
}
return 0;
}
Problem G. 智乃与模数
yysy我觉得这个题难度不低,起码对我这种数学辣鸡来说不是那么容易想出来的,这种题我一般直接打表规律,然后没看出来规律。
思路比较难的可能就是二分第k个数,然后用数论分块去做就好了,要注意边界问题。我们在处理当前块有多少个i是符合
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
bool check(int mid) {
int sum = 0;
for (int l = 1, r; l <= n; l = r + 1) {
if (n / l == 0) break;
r = min(n / (n / l), n);
int c = n / l;
int a = n - n / l * l;
if (a < mid)continue;
sum += min(r - l + 1, (n - mid) / c - l + 1);
}
if (sum >= m) return 1;
return 0;
}
int cal(int c,int l,int r) {
int s1 = 0, s2 = 0;
s1 = (n + n - (l) * c) * (l - 1) / 2;
s2 = (n + n - (r + 1) * c) * (r) / 2;
return s2 - s1;
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
int l = 0, r = n + 1;
while (l + 1 != r) {
int mid = l + r >> 1;
if (check(mid))l = mid;
else r = mid;
}
int x = l + 1;
int cnt = 0;
// cc(x);
int ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
if (n / l == 0) break;
r = min(n / (n / l), n);
int c = n / l;
int a = n - n / l * l;
if (a < x)continue;
cnt += min(r - l + 1, (n - x) / c - l + 1);
ans += cal(c, l, min(r, (n - x) / c));
}
ans += (m - cnt) * (x - 1);
cc(ans);
}
return 0;
}
Problem H. 智乃与黑白树
最痛苦的一集。由于swap了x和y,导致后面脑子混乱在一个地方把y写成了x,在赛后一分钟看出来然后AC了,很是心痛。明明在赛前一分钟看出来了另一个bug,本以为皆大欢喜,没想到还是WA,很是心痛啊,只能说拼尽全力并未战胜。
首先这种题很典啊。树形dp。
要求的是断开边之后,剩下的根x和y所在的树的贡献会是多少。
我们考虑,假设x和y连接起来所新产生的贡献是de,连接之前两个树自身的贡献分别是qx和qy,连接后的一整个树的贡献是sum。那么de+qx+qy=sum。
我们知道,x和y中,我们设y是x的子节点。在树形dp之后,我们求出来一个点的子树的贡献是比较好算的(经典的树形dp,可以算出来每一个点的子树的贡献),那么x和y断开后,这个贡献其实就是qy。de其实也比较好算,就是两个树合并时怎么计算,到时候推式子就好了。sum就是1号节点的子树的贡献。那么de,qy,sum都已知了,qx也就已知了。
具体式子的推理写在代码里了。详情直接看代码的注释。
//pi数组是代表当前点的与父节点所连接的那条边的编号是多少
int son[N], dep[N], pi[N];
//f数组是点i的子树的黑节点个数之和,白节点个数之和。0是白,1是黑。
int f[N][2];
//g数组是点i的子树的黑节点到点i的路径的和,白节点到点i的路径和。
//g1数组是这一个树以i给根的话,黑节点到i的路径之和。
int g[N][2], g1[N][2];
//dp1数组是点i的子树的贡献。
int dp1[N];
for (auto &[y, val,pid]: A[x]) {
if (y == pa) continue;
pi[y] = pid;
dfs(y, x);
son[x] += son[y];
//这里就是最核心的地方。理解了这个其他地方也不会有问题了。建议草稿纸耐心写写。
dp1[x] += f[x][1] * f[y][0] + f[y][0] * g[x][1] + g[y][0] * f[x][1];
dp1[x] += f[x][0] * f[y][1] + f[y][1] * g[x][0] + g[y][1] * f[x][0];
dp1[x] += dp1[y];
f[x][0] += f[y][0];
f[x][1] += f[y][1];
g[x][0] += g[y][0] + f[y][0];
g[x][1] += g[y][1] + f[y][1];
}
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int ksum = 0;
bool kval[N];
//--------------------------------------------------------------------------------
//struct or namespace:
struct ed {
int x;
int y;
};
vec<ed> e;
namespace z {
struct ED {
int y;
int val;
int pid;
};
vector<ED> A[N];
//pi数组是代表当前点的与父节点所连接的那条边的编号是多少
int son[N], dep[N], pi[N];
//f数组是点i的子树的黑节点个数之和,白节点个数之和。0是白,1是黑。
int f[N][2];
//g数组是点i的子树的黑节点到点i的路径的和,白节点到点i的路径和。
//g1数组是这一个树以i给根的话,黑节点到i的路径之和。
int g[N][2], g1[N][2];
//dp1数组是点i的子树的贡献。
int dp1[N];
void dfs(const int x, const int pa) {
son[x] = 1;
dep[x] = dep[pa] + 1;
if (kval[x]) f[x][1] = 1, f[x][0] = 0;
else f[x][0] = 1, f[x][1] = 0;
g[x][0] = g[x][1] = 0;
dp1[x] = 0;
for (auto &[y, val,pid]: A[x]) {
if (y == pa) continue;
pi[y] = pid;
dfs(y, x);
son[x] += son[y];
dp1[x] += f[x][1] * f[y][0] + f[y][0] * g[x][1] + g[y][0] * f[x][1];
dp1[x] += f[x][0] * f[y][1] + f[y][1] * g[x][0] + g[y][1] * f[x][0];
dp1[x] += dp1[y];
f[x][0] += f[y][0];
f[x][1] += f[y][1];
g[x][0] += g[y][0] + f[y][0];
g[x][1] += g[y][1] + f[y][1];
}
}
void dfs1(const int x, const int pa) {
for (auto &[y, val,pid]: A[x]) {
if (y == pa) continue;
g1[y][0] = g1[x][0] + (f[1][0] - f[y][0]) - (f[y][0]);
g1[y][1] = g1[x][1] + (f[1][1] - f[y][1]) - (f[y][1]);
dfs1(y, x);
}
}
void clear(const int &n) {
rep(i, 1, n) {
A[i].clear();
}
}
void add(const int &x, const int &y, int c,int pid) {
A[x].push_back({y, c, pid});
// A[y].push_back({x, c, pid});
}
};
PII ans[N];
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n;
z::clear(n);
rep(i, 1, n) {
char a;
cin >> a;
if (a == 'b') kval[i] = 1;
else kval[i] = 0;
}
rep(i, 1, n-1) {
int a, b;
cin >> a >> b;
e.push_back({a, b});
z::add(a, b, 1, i);
z::add(b, a, 1, i);
}
z::dfs(1, 0);
ksum = z::dp1[1];
using z::g1;
using z::g;
g1[1][1] = g[1][1];
g1[1][0] = g[1][0];
z::dfs1(1, 0);
// cc(z::dp1[1]);
int len = e.size();
rep(i, 0, len-1) {
int x = e[i].x;
int y = e[i].y;
using z::f;
using z::g;
using z::g1;
if (z::dep[x] < z::dep[y]) {
ans[i].second = z::dp1[y];
int fx0 = f[1][0] - f[y][0];
int fx1 = f[1][1] - f[y][1];
int gx0 = g1[x][0] - g[y][0] - f[y][0];
int gx1 = g1[x][1] - g[y][1] - f[y][1];
int de = 0;
de += fx1 * f[y][0] + f[y][0] * gx1 + g[y][0] * fx1;
de += fx0 * f[y][1] + f[y][1] * gx0 + g[y][1] * fx0;
ans[i].first = ksum - de - ans[i].second;
// if (i == 1) {
// }
// cc(de);
// if (i == 0) cc(de);
}
else {
swap(x, y);
ans[i].first = z::dp1[y];
int fx0 = f[1][0] - f[y][0];
int fx1 = f[1][1] - f[y][1];
int gx0 = g1[x][0] - g[y][0] - f[y][0];
int gx1 = g1[x][1] - g[y][1] - f[y][1];
int de = 0;
de += fx1 * f[y][0] + f[y][0] * gx1 + g[y][0] * fx1;
de += fx0 * f[y][1] + f[y][1] * gx0 + g[y][1] * fx0;
ans[i].second = ksum - de - ans[i].first;
// cc(de);
}
// if (i == 1) {
cc(ans[i].first, ans[i].second);
// }
}
// cc("asd");
}
return 0;
}
Problem K. 智乃的逆序数
觉得这个题很弱智啊,几乎没什么难度,个人认为应该和E一个难度。
直接从小到大的去放置每一个序列,然后直接暴力交换就好了,反正也不会T。真的怀疑数据范围不应该是1e5那里吗?
可惜赛时最后去搞H了,也没有去写K。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
map<int,int> mp;
int len = 0;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
struct node {
int mmin;
int len;
vec<int> A;
};
node Q[N];
int val[N];
int ksum = 0;
int dfs() {
int sum = 0;
rep(i, 1, len) {
rep(j, i+1, len) {
if (val[i] > val[j]) sum++;
}
}
return sum;
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n >> m;
rep(i, 1, n) {
int a;
cin >> a;
int mmin = INF;
Q[i].len = a;
rep(j, 1, a) {
int b;
cin >> b;
Q[i].A.push_back(b);
cmin(mmin, b);
mp[b] = i;
}
Q[i].mmin = mmin;
}
sort(Q + 1, Q + n + 1, [&](const node &a, const node &b) {
return a.mmin < b.mmin;
});
rep(i, 1, n) {
for (auto &x: Q[i].A) {
val[++len] = x;
}
}
ksum = dfs();
if (ksum > m) {
cc("No");
continue;
}
m -= ksum;
rep(i, 1, len) {
rep2(j, i-1, 1) {
if (m <= 0) goto Z;
int l = val[j], r = val[j + 1];
if (l < r and mp[l] != mp[r]) {
m--;
swap(val[j], val[j + 1]);
}
}
}
Z:;
if (m != 0) {
cc("No");
continue;
}
cc("Yes");
// cc(m);
rep(i, 1, len) {
cout << val[i] << ' ';
}
cout << endl;
// int ll = dfs();
// cc(ksum, ll);
}
return 0;
}
Problem L. 智乃的三角遍历
自己喜欢怎么走就怎么去写就好了,这种很多。
我的路径是先走到左下角,然后走到右下角,然后向左走,往上走一个,再往下走一个,像(W)一样,走到头了就走一条直线到右边的尽头,如此往复。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[11][11];
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
cin >> n;
if (n == 1) {
cc("Yes");
cc("1 2 3 1");
}
else if (n == 2) {
cc("Yes");
cc("1 3 6 5 2 4 5 3 2 1");
}
else {
n += 1;
int cnt = 0;
rep(i, 1, n) {
rep(j, 1, i) {
A[i][j] = ++cnt;
}
}
vec<int> ans;
rep(i, 1, n) ans.push_back(A[i][1]);
rep(i, 2, n) ans.push_back(A[n][i]);
int r = n;
rep2(r, n, 2) {
rep2(l, r-1, 1) {
if (l == 1) {
ans.push_back(A[r - 1][l]);
// if (r - 1 != 1)
rep(i, l+1, r-1) ans.push_back(A[r - 1][i]);
break;
}
ans.push_back(A[r - 1][l]);
ans.push_back(A[r][l]);
}
}
cc("Yes");
cc(ans);
}
}
return 0;
}
Problem M. 智乃的牛题
弱智题就不说了。
//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
//--------------------------------------------------------------------------------
//struct or namespace:
//--------------------------------------------------------------------------------
int A[N];
int f(char x) {
return x - 'a';
}
signed main() {
fileRead();
kuaidu();
T = 1;
//cin >> T;
while (T--) {
string s;
cin >> s;
for (auto &x: s) {
A[x - 'a']++;
}
int fl = 1;
if (A[f('c')] != 1) fl = 0;
if (A[f('d')] != 1) fl = 0;
if (A[f('e')] != 1) fl = 0;
if (A[f('n')] != 1) fl = 0;
if (A[f('o')] != 2) fl = 0;
if (A[f('r')] != 1) fl = 0;
if (A[f('w')] != 1) fl = 0;
if (fl)cc("happy new year");
else cc("I AK IOI");
}
return 0;
}
PostScript
阿巴巴阿巴,H题可惜。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验