A

https://www.luogu.com.cn/problem/P16232

思路:找规律

(n+1)/2

B

思路:打表,找规律,bfs,阶乘逆元

来自B站up yeVegetable https://www.bilibili.com/video/BV1PzDkBsEzU/?spm_id_from=333.1391.0.0

打表找规律,很神奇,最后是个杨辉三角(遇到跟2有关的选项杨辉三角)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

void bfs(int n) {
    vector<int>s(n);
    queue <vector<int>>q;
    map<vector<int>,int>dis;
    q.push(s);
    dis[s] = 0;
	while (q.size()) {
		auto u = q.front();
		q.pop();
		for (int i = 0; i < n; i++) {
            auto v = u;
            if (dis[u] % 2 == 0) {
				for (int j = i; j < n; j++) v[j]^=1;
            }
			else for (int j = 0; j <= i; j++) v[j] ^= 1;
			if (!dis.count(v)) {
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
	}
	int ans = 0;
	for (auto& [v, d] : dis) {
		for (auto x : v)cout<<x<<" ";
		cout << endl;
		cout << "步数: " << d << endl;
		ans += d;
	}
	cout << "总步数" << ans << endl;
}

void solve()
{
	for (int i = 1; i <= 5; i++) {
		cout << "n = " << i << endl;
		bfs(i);
	}
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;//cin>>t;
    while (t--)solve();

    return 0;
}
n 操作数 (0- ∞ )操作数的个数
1 0 1 1 1
2 0 1 2 1 1 2 1
3 0 1 3 1 2 2 2 1 1 3 3 1
4 0 1 3 1 3 4 3 1 2 2 3 2 2 2 2 1 1 4 6 4 1
... ... ...

得出公式

然后n=2026求解

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

ll fact[3000],inv[3000];
const int mod = 998244353;
ll fastpow(ll a, ll b) {
	ll res = 1;
	while (b) {
		if (b & 1)res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

ll calc(int n, int m) {
	if (m > n)return 0;
	return fact[n] * inv[n - m] % mod * inv[m] % mod;
}

void solve()
{
	fact[0] = 1;
	for (int i = 1; i <= 2026; i++)fact[i] = fact[i - 1] * i % mod;
	inv[2026] = fastpow(fact[2026], mod - 2);
	for (int i = 2025; i >= 0; i--)inv[i] = inv[i + 1] * (i + 1) % mod;
	ll sum = 0;
	for (int i = 0; i <= 2026; i++) {
		sum = (sum + calc(2026, i)*i%mod) % mod;
	}
	cout << sum << endl;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;//cin>>t;
    while (t--)solve();

    return 0;
}

C

https://www.luogu.com.cn/problem/P16234

思路:无

即序列元素都相同

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

void solve()
{
    ll n, x, y; cin >> n >> x >> y;
    if (x > y)cout << 0 << endl;
    else cout << y - x + 1 << endl;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;cin>>t;
    while (t--)solve();

    return 0;
}

D

https://www.luogu.com.cn/problem/P16235

思路:数论

总和sum%5==0,队员的最大数量mx<=sum/5 证:if mx>sum/5,需要总人数mx*5>sum,不成立,所以mx<=sum/5

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
#define endl '\n'

void solve()
{
    ll n; cin >> n;
    ll maxx = -1, sum = 0;
    for (int i = 0; i < n; i++) {
        ll num; cin >> num;
        maxx = max(maxx, num);
        sum += num;
    }
    if (sum % 5 != 0 || maxx > sum / 5) {
        cout << "F" << endl;
    }
    else {
        cout << "T" << endl;
    }
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1; cin >> t;
    while (t--)solve();

    return 0;
}

E

https://www.luogu.com.cn/problem/P16236

思路:枚举,前缀和

注意到在m个'?'里左边为'L'右边为'Q'永远是最优的,在m里枚举分界线的位置

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
#define endl '\n'

int l[100010], r[100010];
void solve()
{
	int n; cin >> n;
	string s; cin >> s;
	vector<int>pos;
	if (s[0] == '?')s[0] = 'L';
	if (s[0] == 'L')l[0] = 1;
	if (s[n - 1] == '?')s[n - 1] = 'Q';
	if (s[n - 1] == 'Q')r[n - 1] = 1;


	for (int i = 1; i < n; i++) {
		l[i] = l[i - 1];
		if (s[i] == 'L')l[i] += 1;
		else if (s[i] == '?') {
			pos.push_back(i);
			s[i] = 'Q';
		}
	}
	for (int i = n - 2; i >= 0; i--) {
		r[i] = r[i + 1];
		if (s[i] == 'Q')r[i] += 1;
	}
	ll res = 0, cntl = 0;
	for (int i = 0; i < n; i++) {
		if (s[i] == 'L')cntl++;
		else res += cntl;
	}
	ll ans = res;

	for (int i = 0; i < pos.size(); i++) {
		ans = max(ans, res += -l[pos[i]] + r[pos[i] + 1] - i);
	}//当前贡献res -(i左边原始'L'的个数)+(i右边'Q'的个数)-(修改增加的'L'的个数
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int t = 1;//cin>>t;
	while (t--)solve();

	return 0;
}

F

https://www.luogu.com.cn/problem/P16237

思路:并查集

!!!!考虑有环,要用并查集求块的数量,未联通的联通块也要联通,所以是cnt+1个连通块需要cnt个跳线,计算cnt个跳线的度,用n个电脑去平摊,f=(2*cnt)/n,f>1,需要某块额外连一个块,0<f<1,保证一个块最多连一个块,f=0,只有一个块。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

int fa[100010];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) { fa[find(x)] = find(y); }

void solve()
{
    int n, m; cin >> n >> m;
	for (int i = 1; i <= n; i++)fa[i] = i;
	for (int i = 0; i < m; i++) {
		int a, b; cin >> a >> b;
		int fa = find(a), fb = find(b);
		if (fa != fb) {
			merge(a, b);
		}
	}
	int cnt = 0;
	for (int i = 1; i <= n; i++)if (fa[i] == i)cnt++;
	cout << --cnt << " " << (2 * cnt + n - 1) / n << endl;
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;//cin>>t;
    while (t--)solve();

    return 0;
}

G

https://www.luogu.com.cn/problem/P16238

思路:最大字段和,前缀和

先求出差值c[i]数组,计算从1到该位置0个数的前缀和s[i],把差值相同的下标用 unordered_map<ll,vector<int>>存起来,遍历差值,求最大字段和ans,最后答案为=ans+总0的个数。 复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

void solve()
{
	int n; cin >> n;
	vector<ll>a(n+1), b(n+1), c(n+1), s(n+1);
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= n; i++) cin >> b[i];
	for (int i = 1; i <= n; i++)c[i] = a[i] - b[i];
	int c0 = 0;
	unordered_map<ll, vector<int>>mp;
	for (int i = 1; i <= n; i++) {
		s[i] = s[i - 1] + (c[i] == 0);
		if (c[i] == 0)c0++;
		else mp[c[i]].push_back(i);
	}
	int ans = 0;
	for (auto& [k, v] : mp) {
		int dp = 1,mx = 1;//初始 就可以选择这一个端点变为合法
		for (int i = 1; i < v.size(); i++) {
			int l = v[i - 1], r = v[i];
			int cnt0 = s[r - 1] - s[l];
			dp = max(1, dp + 1 - cnt0);//字段和:加上当前端点,减去中间的0
			mx = max(mx, dp);
		}
		ans = max(ans, mx);
	}
	cout << ans + c0 << endl;//加上所有的0
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;//cin>>t;
    while (t--)solve();

    return 0;
}

H

https://www.luogu.com.cn/problem/P16239

思路:二分,优先队列

  1. 核心数学原理:边际收益我们要最大化乘积 。假设我们当前已经分配了一些天数,现在多出 1 天,该给谁?给队员 ,乘积会变为原来的:为了让乘积增幅最大,我们要选择 最大 的人。这等价于选择 最小 的人。我们将这个值拆解:。这就变成了一个“补短板”的问题:谁的“实力/天赋”比值(加上已训练天数)最小,我们就训练谁。
  2. 二分搜索:寻找“水位线”因为 太大,不能一个个加。代码想象了一个“水位线” :代码中 c[i] = a[i] / b[i] 是每个队员初始的“基础水位”。如果我们想让所有队员的“水位”至少达到 ,那么第 名队员需要的训练天数 (如果 )。chk(p) 函数:计算如果要达到水位 ,总共需要多少天。如果总天数 ,说明水位 还可以更高。通过二分查找,代码找到了在 天内能让所有人达到的最高整数水位
  3. 处理余数:精细排序二分只能处理整数部分 c[i],但实际的“水位”是 。即使整数部分相同,余数 越小的人,其实际数值越小,也就越优先需要训练。排序逻辑(代码第 52-53 行):比较两个人的“分数”:。代码通过 r[x] * b[y] < r[y] * b[x](交叉相乘)来避免浮点误差。余数比例越小的人,在分配完基础天数后,优先获得额外的 1 天训练机会。
  4. 计算结果最后,根据二分出的水位 和排序补齐的额外天数,算出每个队员最终的实力值 ,累乘并取模得到答案。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull  unsigned long long
#define endl '\n'

const ll mod = 998244353;

void solve()
{
	int n; cin >> n;
    ll m; cin >> m;
    vector<ll> a(n), b(n), c(n), r(n);
	ll mx = 0;
	for (int i = 0; i < n; i++) {
		cin >> a[i] >> b[i];
		if (b[i] > 0) {
			c[i] = a[i] / b[i]; // 实力比值
			r[i] = a[i] % b[i]; // 余数
			mx = max(mx, c[i]); // 记录最大实力比值,二分上界
		}
		else {// b[i]为0时,c[i]设为一个极大值,使其不参与二分补齐
			c[i] = 9e18;
		}
	}

    // 二分检测函数:达到比值 p 需要多少天
    auto check = [&](ll p) {
        ll s = 0;
        for (int i = 0; i < n; i++) {
            if (p > c[i]) {
                s += p - c[i];
                if (s > m) return s; // 防止累加溢出,提前退出
            }
        }
        return s;
    };

    // 1. 二分查找最大可以达到的整数比值 p
	ll l = 0, h = mx + m + 1;//上界为最大比值加上所有天数,保证足够大
    while (l < h) {
        ll mid = (l + h + 1) >> 1;
        if (check(mid) <= m) l = mid;
        else h = mid - 1;
    }

	ll p = l; // 最终比值
    ll used = check(p);
    ll rem = m - used; // 剩下的天数,每人最多再分1天

    // 2. 按余数比例 r/b 从小到大排序
    // 比例越小,说明在当前比值下其实际实力越弱,优先补齐
    vector<int> id;
    for (int i = 0; i < n; i++) {
        if (b[i] > 0 && c[i] <= p) {
            id.push_back(i);
        }
    }

    sort(id.begin(), id.end(), [&](int x, int y) {
        // 比较 r[x]/b[x] < r[y]/b[y] -> r[x]*b[y] < r[y]*b[x]
        return (ll)r[x] * b[y] < (ll)r[y] * b[x];
        });

    // 3. 分配额外的训练天数
    vector<int> ad(n, 0);
    for (int i = 0; i < id.size() && i < rem; i++) {
        ad[id[i]] = 1;
    }

    // 4. 计算最终结果
    ll ans = 1;
    for (int i = 0; i < n; i++) {
        ll k = 0;//天数
        if (b[i] > 0 && p > c[i]) { // 跳过不增长的和已达标的
			k = p - c[i];//补齐到比值 p 需要的天数
        }
		if (ad[i]) k++; // 如果分配了额外天数,再加1
        // 最终实力 v = a + k*b
        ll v = (a[i] + k * b[i]%mod)%mod;
        ans = ans * v % mod;
    }

    cout << ans << endl;

}

int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t = 1;//cin>>t;
    while (t--)solve();

    return 0;
}

总结: 做题时想法很多,接近30%~80%,但是不能实际解决问题,在代码细节上还不熟练,代码从想法到实现不成熟; 目标评估:思维67% 实现50%,目前刷题200个,目标400个。