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。然后我们假设l端点已知,那么mid时间过后,在l+mid+mid位置之前的向左的小球都会碰到,这段区间我们找到对应的小球数组的位置上,在那段区间我们求和就是碰到的球的个数了。时间复杂度是O(nlognlogn)

//--------------------------------------------------------------------------------
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是符合(nci)>=mid   (c=n/l),记得要去判断一下首先是如果个数是0怎么办,还有就是如果个数大于r-l+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:

//--------------------------------------------------------------------------------

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题可惜。

posted @   AdviseDY  阅读(150)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示