2025牛客寒假演算法基礎集訓營2 題解

AdviseDY發表於2025-01-24

Preface

作為一名半退役選手,個人感覺本場難度不是太大,感覺和去年比難度貌似是下降了。在打的時候實在是由於亂七八糟的原因(包括不限於眼瞎,眼瞎,還有眼瞎),打得一般般。

感覺本場模擬題偏多,實在是吃屎

我會在程式碼一些有必要的地方加上註釋,簽到題可能一般就不會寫了.

以下是程式碼火車頭:

#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. 一起奏響歷史之音!

開場就\(WA\)了兩發,詳情不說了。好好看題就好。

//--------------------------------------------------------------------------------
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--) {
		bool fl = 0;
		rep(i, 1, 7) {
			int a;
			cin >> a;
			if (a == 4 or a == 7) fl = 1;

		}
		if (!fl) cc("YES");
		else cc("NO");

	}
	return 0;
}

Problem B. 能去你家蹭口飯吃嗎

實際就是求中間的數,即\(A[(n+1+1)/2]\)(偶數就是向上取整),這個數減一就好了。

//--------------------------------------------------------------------------------
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[N];
//--------------------------------------------------------------------------------
//struct or namespace:

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

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n;
		rep(i, 1, n) cin >> A[i];
		sort(A + 1, A + n + 1);
		// rep(i, 1, n) cc(A[i]);

		int val = A[(n + 1 + 1) / 2] - 1;
		cc(val);
	}
	return 0;
}

Problem C. 字串外串

對於這種構造題,我真的講不出為什麼。只能說是直覺。實則是瞎猜。猜著猜著就過了,不要問我為什麼這樣想。

思考的時候發現,我們不妨固定那一對決定了可愛度的字元\(a\),在\(m\)的位置,相對應的,由於必須要有一對,所以我們也可以在對稱的位置\(n-m+1\)的位置也放一個\(a\)。然後分類討論一下,當\(n-m+1<=m\)的時候,那麼我們空著的那些位置,我們發現我們可以都填\(a\),但是在\(n-m+1>m\)的時候,中間並不能填\(a\)。然後別的位置我們可以也拿一對字元\(b\)來寫寫畫畫,然後兩邊只能一邊一個,或者是隻在一邊放,另一邊不放。如果放了三個或者同一側放了兩個顯然是不行的。所以不可行的情況也能寫出來了,當字符集(26個)不夠用的時候,就是\(no\)的情況,\(yes\)的情況多討論討論就好了。

//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
char s[N];
//--------------------------------------------------------------------------------
//struct or namespace:

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

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	cin >> T;
	while (T--) {
		cin >> n >> m;

		if (n == m) {
			cc("NO");
			continue;
		}


		if (m >= n - m + 1) {
			if (n - m - 1 <= 25 and n >= 4 and m >= 3) {
				cc("YES");
				s[1] = 'a';
				s[n] = 'a';
				rep(i, n-m+1, m) s[i] = 'a';
				rep(i, 2, n-m+1-1) {
					s[i] = 'a' + i - 1;
					s[n - i + 1] = s[i];
				}

				rep(i, 1, n) { cout << s[i]; }

				cout << endl;
			}
			else {
				cc("NO");
			}
		}
		else {
			if (m - 1 + (n - m - m) <= 25) {

				cc("YES");

				int k = m - 1 + (n - m - m);
				int x = n - k - 2, y = k - x;
				rep(i, 1, x) { cout << char('a' + i); }
				cout << 'a';
				rep(i, x+1, x+y) { cout << char('a' + i); }
				cout << 'a';
				rep(i, 1, x) { cout << char('a' + i); }

				cout << endl;

			}
			else {
				cc("NO");
			}
		}
	}
	return 0;
}

Problem D. 字串裡串

我們可以想到,當固定一個連續子串的時候,去找尋一個連續的子序列到底有沒有,我們只需要判斷這個子串的右側是否還有一個子串右端點的那個字元就可以了,左側也同理。

以下都只討論右側:判斷是否右側有沒有字元可以提前字尾和預處理出來。

然後首先二分長度,然後就暴力\(O(n)\)的掃一遍就好了。

//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int pre[N][31];
int suf[N][31];
string s;

//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------
bool check(int len) {
	rep(i, 1, n) {
		int l = i, r = i + len - 1;
		if (l < 1 or r > n) break;
		if (pre[l - 1][s[l] - 'a'] or suf[r + 1][s[r] - 'a']) {
			return 1;
		}
	}
	return 0;
}

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n;
		cin >> s;
		s = ' ' + s;

		rep(i, 1, n) {
			rep(j, 0, 25) pre[i][j] = pre[i - 1][j];
			pre[i][s[i] - 'a'] = 1;
		}
		rep2(i, n, 1) {
			rep(j, 0, 25) suf[i][j] = suf[i + 1][j];
			suf[i][s[i] - 'a'] = 1;
		}

		int ans = 0;

		int l = 0, r = n;
		while (l + 1 != r) {
			int mid = l + r >> 1;
			if (check(mid)) l = mid;
			else r = mid;
		}
		cc(l);

	}
	return 0;
}

Problem E. 一起走很長的路!

一個很典典的題,簡簡單單推式子就好了,比\(C\)題簡單不少。

設字首和\(pre_i=a_1+a_2+...+a_i\),那麼需要滿足條件:\(pre_i-pre_{l-1}>=a_{i+1}\)\(l<=i<=r-1\)

式子挪一下,就是\(pre_i-a_{i+1}>=pre_{l-1}\)\(l<=i<=r-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 mmin = INF;
 
        void apply(int k) {
            mmin = k;
        }
 
        friend info operator+(const info &q1, const info &q2) {
            info q;
            q.mmin = min(q1.mmin, q2.mmin);
            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;
int A[N], pre[N], dis[N];
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        cin >> n;
        int q;
        cin >> q;
        rep(i, 1, n) {
            cin >> A[i];
            pre[i] += A[i];
            pre[i] += pre[i - 1];
        }
        seg.clear(1, n - 1);
 
        rep(i, 1, n-1) {
            dis[i] = pre[i] - A[i + 1];
            seg.add(i, i, dis[i]);
        }
 
        rep(i, 1, q) {
            int l, r;
            cin >> l >> r;
            int tem = seg.qry(l, r - 1).mmin;
            int ans = 0;
            if (tem < pre[l - 1]) {
                ans = pre[l - 1] - tem;
            }
            cc(ans);
        }
 
    }
    return 0;
}

Problem F. 一起找神秘的數!

稍微畫一畫就能發現,滿足條件需要\(x=y\)

具體推的話,可以用韋恩圖畫一下就好了,記住,‘或’是求並集,異或是去重,and 是求交集。

//--------------------------------------------------------------------------------
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[N];
//--------------------------------------------------------------------------------
//struct or namespace:

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

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n;
		rep(i, 1, n) {
			int a, b;
			cin >> a >> b;
			cc(b - a + 1);
		}
	}
	return 0;
}

Problem G. 一起鑄最好的劍!

直接模擬就好了,不要直接用\(pow\)函式就好。

//--------------------------------------------------------------------------------
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[N];
//--------------------------------------------------------------------------------
//struct or namespace:

//--------------------------------------------------------------------------------
int dfs(int b,int a) {

	if (b == 1) {
		return 1;
	}

	int ans = 0;
	int mmax = INF;
	int l = 0;
	int bas = b;
	b = b, l = 1;
	while (b <= a) {
		if (abs(b - a) < mmax) mmax = abs(b - a), ans = l;
		l++;
		b = b * bas;
	}
	if (abs(b - a) < mmax) mmax = abs(b - a), ans = l;
	return ans;
}

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n;
		rep(i, 1, n) {
			int a, b;
			cin >> a >> b;
			cc(dfs(b, a));
		}
	}
	return 0;
}

Problem H. 一起畫很大的圓!

最吃屎的時候,交了\(12\)發才過,還是最後蒙過去的。

首先先讀題,邊界!一開始讀題讀成了內部也可以,然後就先固定了兩個點在斜線,然後找了最接近斜線的橫座標的每一個點去判斷,時間複雜度按理來說是橫座標或者縱座標的差值,\(1\)秒應該是能勉強跑過才對的,但是\(wa\)了,要不就是\(t\)了。

然後發現是在邊界上...,但是依舊不會,腦子已經秀逗了。

最後暴力列舉了角落邊界和中間邊界的位置,然後跑過的。(其實也是有直覺是感覺兩個點在角落,一個點在另一個同側的角落,但是不確定)

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int ans_x[4], ans_y[4];
double ans = 0;
int a, bb, c, d;

//--------------------------------------------------------------------------------
//struct or namespace:

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

bool pan(int x,int y) {
	if (x < a or x > bb or y < c or y > d) return 1;
	return 0;

}

double radi(int x11,int y11,int x22,int y22,int x33,int y33) {
	if (pan(x11, y11)) return 0;
	if (pan(x22, y22)) return 0;
	if (pan(x33, y33)) return 0;
	if (x11 == x22 and y11 == y22) return 0;
	if (x11 == x33 and y11 == y33) return 0;
	if (x22 == x33 and y22 == y33) return 0;
	// cc(x11, y11);
	// cc(x22, y22);
	// cc(x33, y33);
	// cc("");
	double A, B, C, D;
	double x1 = x11, y1 = y11, x2 = x22, y2 = y22, x3 = x33, y3 = y33;

	A = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2;
	B = (x1 * x1 + y1 * y1) * (y3 - y2) + (x2 * x2 + y2 * y2) * (y1 - y3) + (x3 * x3 + y3 * y3) * (y2 - y1);
	C = (x1 * x1 + y1 * y1) * (x2 - x3) + (x2 * x2 + y2 * y2) * (x3 - x1) + (x3 * x3 + y3 * y3) * (x1 - x2);
	D = (x1 * x1 + y1 * y1) * (x3 * y2 - x2 * y3) + (x2 * x2 + y2 * y2) * (x1 * y3 - x3 * y1) + (x3 * x3 + y3 * y3) * (
		    x2 * y1 - x1 * y2);
	if (!A) return 0;
	double r = sqrt((B * B + C * C - 4 * A * D) / (4 * A * A));
	if (r > ans) {
		ans = r;
		ans_x[0] = x11, ans_x[1] = x22, ans_x[2] = x33;
		ans_y[0] = y11, ans_y[1] = y22, ans_y[2] = y33;

	}
	return r;
}


void dfs(int a,int b,int c,int d) {

	int x1 = a, y1 = d;
	int x2 = (a + b + 1) / 2, y2 = d;
	int x3 = b, y3 = d;

	rep(d1, 0, 100) {
		rep(d2, 0, 100) {
			rep(d3, 0, 10) {

				radi(x1, y1 - d1, x2 - d2, y2, x3, y3 - d3);
			}
		}
	}


	x1 = a, y1 = d;
	x2 = a, y2 = d;
	x3 = b, y3 = d;

	rep(d1, 0, 100) {
		rep(d2, 0, 100) {
			rep(d3, 0, 10) {
				radi(x1, y1 - d1, x2 + d2, y2, x3, y3 - d3);
			}
		}
	}


	x1 = a, y1 = c;
	x2 = a, y2 = (c + d + 1) / 2;
	x3 = a, y3 = d;

	rep(d1, 0, 100) {
		rep(d2, 0, 100) {
			rep(d3, 0, 10) {

				radi(x1 + d1, y1, x2, y2 - d2, x3 + d3, y3);
			}
		}
	}

	x1 = a, y1 = c;
	x2 = a, y2 = c;
	x3 = a, y3 = d;

	rep(d1, 0, 100) {
		rep(d2, 0, 100) {
			rep(d3, 0, 10) {

				radi(x1 + d1, y1, x2, y2 + d2, x3 + d3, y3);
			}
		}
	}

}


signed main() {
	fileRead();
	kuaidu();
	T = 1;
	cin >> T;
	while (T--) {
		cin >> a >> bb >> c >> d;

		ans = 0;
		// ans_x = 0, ans_y = 0;
		dfs(a, bb, c, d);

		rep(i, 0, 2) {
			cc(ans_x[i], ans_y[i]);
		}

	}
	return 0;
}

Problem I. 一起看很美的日落!

一個典典的樹形dp,但是賽時沒有時間做了,應該是有銅+的難度的。

首先我們對於每一位可以獨立看待,於是每個點的權值變成了0或者1,然後經典樹形dp。

\(dp_x\)是包含了\(x\)的聯通塊的\(ans\)\(f_{x,0}\)是聯通塊裡0的個數,\(f_{x,1}\)同理,\(g_x\)是聯通塊大小。

然後考慮如何合併兩個聯通塊:

dp[x] += dp[x] * g[y] + dp[y] * g[x];
dp[x] += f[x][0] * f[y][1] + f[y][0] * f[x][1];
f[x][0] += f[y][0] * g[x] + f[x][0] * g[y];
f[x][1] += f[y][1] * g[x] + f[x][1] * g[y];
g[x] += g[x] * g[y];

我覺得式子很顯然,大家看了就曉得了。程式碼很簡潔,順手抄了個題解的取模類。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;

int d[N], val[N];

//--------------------------------------------------------------------------------
//struct or namespace:
template<const int T>
struct ModInt {
	const static int mod = T;
	int x;

	ModInt(int x = 0) : x(x % mod) {
	}

	int val() { return x; }

	ModInt operator +(const ModInt &a) const {
		int x0 = x + a.x;
		return ModInt(x0 < mod ? x0 : x0 - mod);
	}

	ModInt operator -(const ModInt &a) const {
		int x0 = x - a.x;
		return ModInt(x0 < 0 ? x0 + mod : x0);
	}

	ModInt operator *(const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
	ModInt operator /(const ModInt &a) const { return *this * a.inv(); }
	bool operator ==(const ModInt &a) const { return x == a.x; };
	bool operator !=(const ModInt &a) const { return x != a.x; };

	void operator +=(const ModInt &a) {
		x += a.x;
		if (x >= mod) x -= mod;
	}

	void operator -=(const ModInt &a) {
		x -= a.x;
		if (x < 0) x += mod;
	}

	void operator *=(const ModInt &a) { x = 1LL * x * a.x % mod; }
	void operator /=(const ModInt &a) { *this = *this / a; }

	friend ModInt operator +(int y, const ModInt &a) {
		int x0 = y + a.x;
		return ModInt(x0 < mod ? x0 : x0 - mod);
	}

	friend ModInt operator -(int y, const ModInt &a) {
		int x0 = y - a.x;
		return ModInt(x0 < 0 ? x0 + mod : x0);
	}

	friend ModInt operator *(int y, const ModInt &a) { return ModInt(1LL * y * a.x % mod); }
	friend ModInt operator /(int y, const ModInt &a) { return ModInt(y) / a; }
	friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x; }
	friend istream &operator>>(istream &is, ModInt &t) { return is >> t.x; }

	ModInt pow(int n) const {
		ModInt res(1), mul(x);
		while (n) {
			if (n & 1) res *= mul;
			mul *= mul;
			n >>= 1;
		}
		return res;
	}

	ModInt inv() const {
		int a = x, b = mod, u = 1, v = 0;
		while (b) {
			int t = a / b;
			a -= t * b;
			swap(a, b);
			u -= t * v;
			swap(u, v);
		}
		if (u < 0) u += mod;
		return u;
	}

};

using MI = ModInt<mod>;

MI dp[N], f[N][2], g[N];

namespace z {
	struct ED {
		int y;
		int val;
	};

	vector<ED> A[N];
	int son[N], dep[N];

	void dfs(const int x, const int pa) {
		g[x] = 1;
		dp[x] = 0;
		if (val[x] == 1) {
			f[x][1] = 1, f[x][0] = 0;
		}
		else {
			f[x][0] = 1, f[x][1] = 0;
		}
		for (auto &[y, val]: A[x]) {
			if (y == pa) continue;
			dfs(y, x);
			dp[x] += dp[x] * g[y] + dp[y] * g[x];
			dp[x] += f[x][0] * f[y][1] + f[y][0] * f[x][1];
			f[x][0] += f[y][0] * g[x] + f[x][0] * g[y];
			f[x][1] += f[y][1] * g[x] + f[x][1] * g[y];
			g[x] += g[x] * g[y];
		}
		// cc(x, f[x][1], f[x][0]);
	}

	void clear(const int &n) {
		rep(i, 1, n) {
			A[i].clear();
		}
	}

	void add(const int &x, const int &y, int c = 1) {
		A[x].push_back({y, c});
	}
};

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


signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {

		cin >> n;
		z::clear(n);
		rep(i, 1, n) cin >> d[i];
		rep(i, 1, n-1) {
			int a, b;
			cin >> a >> b;
			z::add(a, b);
			z::add(b, a);
		}
		MI ans = 0;
		rep(j, 0, 31) {
			rep(i, 1, n) val[i] = (d[i] >> j) & 1;
			z::dfs(1, 0);
			rep(i, 1, n) {
				MI tem = (1ll << j);
				ans += dp[i] * tem;
			}
		}
		ans *= 2;
		cc(ans);
	}
	return 0;
}

Problem J. 資料時間?

模擬題模擬題,但是題意不明? 因為感覺忽視了天數,常理來說應該同一個人同一個時間段不同的天數應該是算作不同的人數才對。不管了,按照題幹模擬就完事了。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
set<string> mp[3];
//--------------------------------------------------------------------------------
//struct or namespace:
void dfs(string &s, string &id) {
	// cc(s);

	int q1, q2, q3;
	string s1 = "", s2 = "", s3 = "";
	s1.push_back(s[0]);
	s1.push_back(s[1]);
	q1 = stoi(s1);

	s2.push_back(s[3]);
	s2.push_back(s[4]);
	q2 = stoi(s2);

	s3.push_back(s[6]);
	s3.push_back(s[7]);
	q3 = stoi(s3);

	// cc(q1, q2, q3);

	if (q1 < 9 and q1 >= 7 || q1 < 20 and q1 >= 18) {
		mp[0].insert(id);
		return;
	}

	if (q1 == 9 and q2 == 0 and q3 == 0) {
		mp[0].insert(id);
		return;
	}

	if (q1 == 20 and q2 == 0 and q3 == 0) {
		mp[0].insert(id);
		return;
	}

	if (q1 < 13 and q1 >= 11) {
		mp[1].insert(id);
		return;
	}

	if (q1 == 13 and q2 == 0 and q3 == 0) {
		mp[1].insert(id);
		return;
	}

	if (q1 < 24 and q1 >= 22 || q1 < 1) {
		mp[2].insert(id);
		return;
	}

	if (q1 == 1 and q2 == 0 and q3 == 0) {
		mp[2].insert(id);
		return;
	}

}

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

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n;
		string ye, yue;
		cin >> ye >> yue;
		while (ye.size() < 2) ye = '0' + ye;
		while (yue.size() < 2) yue = '0' + yue;

		rep(i, 1, n) {
			string id;
			cin >> id;
			string a, b;
			cin >> a >> b;
			string ye1 = "", yue1 = "";
			rep(i, 0, 3) ye1.push_back(a[i]);
			rep(i, 5, 6) yue1.push_back(a[i]);
			// cc(ye1, yue1);
			if (ye1 != ye or yue1 != yue) continue;
			dfs(b, id);
		}
		rep(i, 0, 2) {
			cout << mp[i].size() << " ";
		}
	}
	return 0;
}

Problem K. 可以分開嗎?

又是一個很臭的模擬,看個樂呵吧。

大體思路就是把每個點的上下左右以及這個點都標記,然後查詢有多少個點被標記了,再減去當前聯通塊的點的個數。

有點容易\(t\),有一點點最佳化手段。在此再計算的,時間複雜度應該不會超時才對,但是由於\(t\)了,於是做了一點點最佳化,詳細看程式碼。

//--------------------------------------------------------------------------------
const int N = 5e2 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
int A[N][N];
bool biao[N][N];
int Q[5] = {0, 0, 1, -1};
int W[5] = {1, -1, 0, 0};
//tem陣列是上述中用來記錄標記的點
bool tem[N][N], lu[N][N];
//--------------------------------------------------------------------------------
//struct or namespace:

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

void ddd(int x,int y) {
	rep(i, 0, 3) {
		tem[x + Q[i]][y + W[i]] = 1;
	}
	tem[x][y] = 1;
}

int dfs(int x,int y) {
	queue<PII> F;
	F.push({x, y});

	int siz = 0;

	siz++;
	biao[x][y] = 1;
	tem[x][y] = 1;
	vec<PII> pa;
	pa.push_back({x, y});
	ddd(x, y);

	while (!F.empty()) {
		auto [x,y] = F.front();
		F.pop();
		// if (biao[x][y]) continue;
		for (int i = 0; i <= 3; i++) {
			int tx = x + Q[i], ty = y + W[i];
			if (tx < 1 or ty < 1 or tx > n or ty > m) continue;
			if (A[tx][ty] == 0) continue;
			if (biao[tx][ty]) continue;
			siz++;
			biao[tx][ty] = 1;
			tem[tx][ty] = 1;
			ddd(tx, ty);
			pa.push_back({tx, ty});
			F.push({tx, ty});
		}
	}
	// cc(siz);
//此處,記錄下來路徑的pa陣列,去列舉這個而不是直接雙重for迴圈,這樣可以快一些。
	int val = 0;
	for (auto &[x,y]: pa) {
		if (tem[x][y] == 1) {
			tem[x][y] = 0;
			val++;
		}
		rep(i, 0, 3) {
			if (x + Q[i] < 1 or x + Q[i] > n) continue;
			if (y + W[i] < 1 or y + W[i] > m) continue;

			if (tem[x + Q[i]][y + W[i]]) {
				tem[x + Q[i]][y + W[i]] = 0;
				val++;
			}
		}
	}

	return val - siz;

}

signed main() {
	fileRead();
	kuaidu();
	T = 1;
	//cin >> T;
	while (T--) {
		cin >> n >> m;
		rep(i, 1, n) {
			rep(j, 1, m) {
				char a;
				cin >> a;
				A[i][j] = a - '0';
			}
		}

		int ans = INF;

		rep(i, 1, n) {
			rep(j, 1, m) {
				if (biao[i][j]) continue;
				if (A[i][j] == 0) continue;
				cmin(ans, dfs(i, j));
			}
		}
		cc(ans);
	}
	return 0;
}

Problem L. 還會再見嗎?

看了題解,虛樹。

於是直接再見。

學虛樹不如繼續搞開發。

Problem M. 那是我們的影子

賽時沒有做出來,賽後看題解發現不算太難。

但是個人感覺難度應該和\(I\)差不多才對。只能說這種題自己總會寫的複雜,感慨大佬寫的程式碼很簡潔。

感覺官方題解寫的很好很清楚,建議去看(不是自己懶了

借鑑了\(Heltion\)的程式碼:

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
string s[4];
int pos[10];
bool vis[N][10];
vec<int> col[3];
 
//--------------------------------------------------------------------------------
//struct or namespace:
 
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n;
        rep(i, 0, 2) cin >> s[i];
 
        rep(j, 0, n-1) {
            rep(i, 0, 9) vis[j][i] = 0;
        }
        rep(i, 0, 2) col[i].clear();
        rep(i, 0, 9) pos[i] = -1;
        int ans = 1;
 
        rep(j, 0, n-1) {
            int tem = 0;
            rep(i, 0, 2) {
                if (s[i][j] == '?') tem++;
                else {
                    int k = s[i][j] - '0';
                    if (vis[j][k]) ans = 0;
                    vis[j][k] = 1;
 
                    if (pos[k] != -1 and pos[k] != j % 3) ans = 0;
                    pos[k] = j % 3;
 
                }
            }
            if (tem == 2) ans *= 2;
            if (tem == 3) ans *= 6;
            ans %= mod;
        }
 
        if (ans == 0) {
            cc(0);
            continue;
        }
 
        int val = 0;
        auto dfs = [&](auto &dfs,int x) {
            if (x == 10) {
                val++;
                return;
            }
            rep(i, 0, 2) {
                if (col[i].size() < 3 and (pos[x] == i or pos[x] == -1)) {
                    col[i].push_back(x);
                    dfs(dfs, x + 1);
                    col[i].pop_back();
                }
            }
        };
        dfs(dfs, 1);
        ans *= val % mod;
        ans %= mod;
        cc(ans);
 
    }
    return 0;
}
 

PostScript

只能說自己吃屎吃得太多,有些題自己寫的太爛,點名批評\(h\)題。

感覺難度比去年真的少了很多,不知道是不是錯覺。

題解如果有誤希望可以指出來。

相關文章