2026-01-04~06 hetao1733837 的刷题笔记

2026-01-04~06 hetao1733837 的刷题笔记

01-04

LG1976 鸡蛋饼

原题链接:鸡蛋饼

分析

bur, 1 e 8 + 7 1e8+7 1e8+7 也是质数?有点恐怖啊......

这是个黄?好在放了我 n 2 n^2 n2,哦,放我 n 2 n^2 n2 就是橙黄,不放就是绿蓝,彳亍......

那我似乎可以做一些类似于钦定得操作,就是设 d p i dp_i dpi 表示有 2 i 2i 2i 个点的方案数。

转移显然从那些小的转移吧,怎么说呢,由于禁止相交,所以,那不就相当于整出来两个多边形吗?那枚举一下岂不是很香?

喔,好卡,先把前面的发了。

那不是爽完了?不太敢写啊。看一眼题解,对不对。

还是回到了卡特兰数......

等一下,我的思路似乎很接近啊?

我的转移方程大概是
f i = ∑ j = 1 i f j × f n − j f_i=\sum\limits_{j=1}^{i}{f_j\times f_{n-j}} fi=j=1∑ifj×fn−j

好像确实长这样......是不是乘个 n − j n-j n−j 更合理?

好像挺对的,但,我,并没有,看出来,这是个 Catalan 数......

似乎也不对,但是,把圆看成多边形,再分成两个多边形是合理而无比正确的。

模数**打错了。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
#define mod 100000007
using namespace std;
const int N = 3005; 
int n, c[N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	c[0] = 1;
	c[1] = 1;
	c[2] = 2;
	for (int i = 3; i <= n; i++){
		for (int j = 0; j < i; j++){
			c[i] = (c[i] + c[i - j - 1] * c[j]) % mod;
		}
	}
	cout << c[n];
}

就这吧,感觉很......难评......

LG3200 [HNOI2009] 有趣的数列

原题链接:[HNOI2009] 有趣的数列

分析

写完这题, Catalan ⁡ \operatorname{Catalan} Catalan 数就只剩一紫一黑了......

这个用公式里的 \operatorname{} 写英文打出来确实好看!

这是绿?™ 模数还不保证质数!

我决定掏出 Catalan ⁡ \operatorname{Catalan} Catalan 数乱艹一下样例。咋是对的啊?为啥啊?但是,问题又来了,怎么求逆元?

还是要复习一下 exgcd ⁡ \operatorname{exgcd} exgcd 的。

有点小累啊......要不直接 he ⁡ \operatorname{he} he?我非常需要知道为什么这个是 Catalan ⁡ \operatorname{Catalan} Catalan 数!

呃......并没有用到 exgcd ⁡ \operatorname{exgcd} exgcd,而是分解质因数。

根据要求,偶数位上的数大于左边偶数位上的,还大于与其相邻左边的那个数,又因为这是一个排列,所以,偶数位上的数大于等于它的下标。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std; 
const int N = 2000005;
int n, p, cnt[N], mn[N];
bool vis[N];
vector<int> e;
int qpow(int a, int b){
	int res = 1;
	while (b){
		if (b & 1)
			res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> p;
	memset(vis, true, sizeof(vis));
	vis[1] = 0;
	for (int i = 2; i <= (n << 1); i++){
		if (vis[i]){
			e.push_back(i);
			mn[i] = i;
		}
		for (int j = 0; j < e.size() && e[j] * i <= (n << 1); j++){
			vis[e[j] * i] = false;
			mn[e[j] * i] = e[j];
			if (i % e[j] == 0)
				break;
		}
	}
	for (int i = 1; i <= n; i++){
		cnt[i] = -1;
	}
	for (int i = n + 2; i <= (n << 1); i++){
		cnt[i] = 1;
	}
	for (int i = (n << 1); i >= 2; i--){
		if (mn[i] < i){
			cnt[mn[i]] += cnt[i];
			cnt[i / mn[i]] += cnt[i];
		}
	}
	int ans = 1;
	for (int i = 2; i <= (n << 1); i++){
		if (mn[i] == i){
			ans = ans * qpow(i, cnt[i]) % p;
		}
	}
	cout << ans;
}

那么,我是否可以总结一下,当有两种甚至多种分讨,或可以转化为两种及多种分讨的计数题,可以考虑Catalan数

明天问一下 elk

01-05

LG1655 小朋友的球

原题链接:小朋友的球

分析

斯特林数板子吧......

先不写高精度,先写一个暴力的式子,我艹™,咋又是高精度?

非常好,有了给出的 20pts 部分分!

好了,高精度直接粘就行了!

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const int M = 200;
struct bigint{
    int len, a[M];
    bigint(int x = 0){
        memset(a, 0, sizeof(a));
        len = 0;
        if (x == 0){
            len = 1;
        } 
        else{
            while (x){
                a[++len] = x % 10;
                x /= 10;
            }
        }
    }
    int &operator[](int i){
        return a[i];
    }
    void flatten(int L){
        len = L;
        for (int i = 1; i <= len; i++){
            a[i + 1] += a[i] / 10;
            a[i] %= 10;
        }
        while (len > 1 && a[len] == 0){
            len--;
        }
    }
    void print(){
        for (int i = max(len, 1); i >= 1; i--){
            printf("%d", a[i]);
        }
    }
};
bigint operator+(bigint a, bigint b){
    bigint c;
    int len = max(a.len, b.len);
    for (int i = 1; i <= len; i++){
        c[i] += a[i] + b[i];
    }
    c.flatten(len + 1);
    return c;
}
bigint operator*(bigint a, int b){
    bigint c;
    int len = a.len;
    for (int i = 1; i <= len; i++){
        c[i] = a[i] * b;
    }
    c.flatten(len + 11);
    return c;
}
int n, m;
bigint s[N][N];
int main(){
    s[0][0] = bigint(1);
    for (int i = 1; i < N; i++){
        s[i][0] = bigint(0);
    }
    for (int i = 1; i < N; i++){
        for (int k = 1; k <= i; k++){
            s[i][k] = s[i - 1][k] * k + s[i - 1][k - 1];
        }
    }
    while (scanf("%d %d", &n, &m) != EOF){
            s[n][m].print();
            printf("\n");
    }
}

那,继续打斯特林数吧!

哥们,现在 Catalan ⁡ \operatorname{Catalan} Catalan 数和 Stirling ⁡ \operatorname{Stirling} Stirling 数一共剩了两紫五黑,看笑了......

先打紫吧......

打不了啊? Stirling ⁡ \operatorname{Stirling} Stirling 数这几个都是卷积, NTT ⁡ \operatorname{NTT} NTT,能打一点的可能是一个 Catalan ⁡ \operatorname{Catalan} Catalan 数的黑......

那......打一下?

打不了啊?

开容斥吧......

LG3349 [ZJOI2016] 小星星

原题链接:[ZJOI2016] 小星星

分析

受着吧......到这个阶段了,是时候写的题几乎全是紫黑了......谁能想到我 25 25 25 年暑假刚过了第一道蓝......

变成树了?给哥们看蒙了。

bur,相当于把图重构成了一棵树?都是啥啊/ll

终于给题看懂了/ll

n ≤ 17 n\le 17 n≤17,指向了状压 DP,由此,可以想到一个暴力 D P DP DP,即 f i , j , S f_{i,j,S} fi,j,S 表示 i i i 号节点映射到 j j j, i i i 子树的编号集合为 S S S 的方案数。

时间复杂度爆了,那么,考虑优化 S S S 这一维,容斥掉重复映射即可。

那么,每次 O ( 2 n ) O(2^n) O(2n) 枚举 { 1 , 2 , ... , n } \{1,2,\dots,n\} {1,2,...,n} 的一个子集,强制树上每个点编号为这个子集,可以有一个 O ( n 3 ) O(n^3) O(n3) 的 D P DP DP,则答案为:

∣ S ∣ = n 的方案数 − ∣ S ∣ = n − 1 的方案数 + ∣ S ∣ = n − 2 的方案数 = ... |S|=n的方案数-|S|=n-1的方案数+|S|=n-2的方案数=\dots ∣S∣=n的方案数−∣S∣=n−1的方案数+∣S∣=n−2的方案数=...

那我也不会 D P DP DP 啊?

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 20;
int n, m, u, v;
bool edge[N][N], vis[N];
int w[N], f[N][N];
int ans = 0;
vector<int> e[N];
int cur;
void dfs(int u, int fa){
    for (auto v : e[u]){
        if (v == fa) 
			continue;
        dfs(v, u);
    }
    for (int i = 1; i <= cur; i++){
        int x = w[i];
        f[u][i] = 1;
        for (auto v : e[u]){
            if (v == fa) 
				continue;
            int sum = 0;
            for (int j = 1; j <= cur; j++){
                int y = w[j];
                if (!edge[x][y]) 
					continue;
                sum += f[v][j];
            }
            f[u][i] *= sum;
        }
    }
}
void work(){
    cur = 0;
    for (int i = 1; i <= n; i++){
        if (vis[i]){
            w[++cur] = i;
        }
    }
    memset(f, 0, sizeof(f));
    dfs(1, 0);
    for (int i = 1; i <= cur; i++){
        if ((n - cur) % 2 == 1){
            ans -= f[1][i];
        } 
		else{
            ans += f[1][i];
        }
    }
}
void DFS(int p){
    if (p == n + 1){
        work();
        return;
    }
    vis[p] = 0;
    DFS(p + 1);
    vis[p] = 1;
    DFS(p + 1);
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m;
    memset(edge, 0, sizeof(edge));
    for (int i = 1; i <= m; i++){
        cin >> u >> v;
        edge[u][v] = edge[v][u] = 1;
    }
    for (int i = 1; i < n; i++){
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    DFS(1);
    cout << ans;
}

LG3270 [JLOI2016] 成绩比较

原题链接:[JLOI2016] 成绩比较

分析

这个可以看出来是容斥,但是......我不会......

手玩样例!手玩不出来/ll

我决定不写了,啥都打不开,高二模考™啊,一点素质都没有......

正解

cpp 复制代码

那么,开始 D P    o f    D P DP\,\,of\,\,DP DPofDP。

网好了,但我不打算写上面那一题了,计数学久了,啥都是萌萌的😊

01-06

CF1658D2 388535 (Hard Version)

原题链接1:CF1658D2 388535 (Hard Version)

原题链接2:D2. 388535 (Hard Version)

分析

朴素二进制分解假了/ll

考虑枚举所有可能答案,然后拿一个 Trie ⁡ \operatorname{Trie} Trie 树维护一下做完了。

没做完......昨天晚上 aoao 给的,然后读题的时候闪过了字典树,没往这个方向想,然后给了个假思路,没过 xyd 的大样例。

盒出来原题,看 LG 的题解全 WA 了......等一下,这题好像是 SPJ......

我是🍬🍬!

并不能过吧......

还是学官方题解吧......

同样枚举答案,不过这次拿了一个 set,可以接受。

法一

若 l l l 为偶数, r r r 为奇数,忽略最后一位,区间除以 2 2 2 递归求解。否则,将 a i a_i ai 与 a i ⊕ 1 a_i\oplus1 ai⊕1 配对,最多剩余两个可能的 x x x 值。

法二

就是字典树。

按法一吧......

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int T;
int a[N], l, r;
set<int> s1, s2;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T;
	while (T--){
		int mul = 1;
		s1.clear();
		cin >> l >> r;
		for (int i = l; i <= r; i++){
			cin >> a[i];
			s1.insert(a[i]);
		}
		while (l % 2 == 0 && r % 2 == 1){
			s2.clear();
			for (auto v : s1)
				s2.insert(v >> 1);
			swap(s1, s2);
			l >>= 1;
			r >>= 1;
			mul <<= 1;
		}
		int ans;
		if (l % 2 == 0)
			ans = r;
		else
			ans = l;
		for (auto v : s1){
			if (s1.find(v ^ 1) == s1.end()){
				int cur = v ^ ans;
				bool flag = true;
				for (auto u : s1){
					flag &= ((cur ^ u) >= l && (cur ^ u) <= r);
				}
				if (flag){
					ans = cur;
					break;
				}
			}
		}
		cout << ans * mul << '\n';
	}
}

题解为啥是错的?

这个代码 D1 也能过!

数数学久了,学会图论对冲一下吧......

我先去吃个饭......

POJ1270 Following Orders

POJ 应该是彻底没了。

大部分题都搬到 openjudge 了。

原题链接:Following Orders

分析

好像是杜克大学 1993 1993 1993 年的题,比 lz 年龄都大了吧......

POJ 没死,只是换了一种方式活着,openjudge 的图标还是 POJ

而且开了 C++17,✋😭🤚

呃......没看懂......也不想看了......

正解

cpp 复制代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
const int N = 30;
int n, a[N], d[N][N], topo[N];
bool vis[N];
int indeg[N];
void dfs(int cnt){
    if (cnt == n){
        for (int i = 0; i < n; i++) 
            cout << (char)(topo[i] + 'a');
        cout << '\n';
        return ;
    }
    for (int i = 0; i < n; i++){
        int u = a[i];
        if (!vis[u] && indeg[u] == 0){
            vis[u] = true;
            topo[cnt] = u;
            for (int j = 0; j < n; j++){
                int v = a[j];
                if (d[u][v]){
                    indeg[v]--;
                }
            }
            dfs(cnt + 1);
            for (int j = 0; j < n; j++){
                int v = a[j];
                if (d[u][v]){
                    indeg[v]++;
                }
            }
            vis[u] = false;
        }
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    string s;
    bool fi = true;
    while (getline(cin, s)) {
        if (s.empty()) 
			continue;
        if (!fi){
            cout << '\n';
        }
        fi = false;
        memset(d, 0, sizeof(d));
        memset(vis, 0, sizeof(vis));
        memset(indeg, 0, sizeof(indeg));
        n = 0;
        for (auto v : s){
            if (v >= 'a' && v <= 'z'){
                a[n++] = v - 'a';
            }
        }
        sort(a, a + n);
        getline(cin, s);
        for (int i = 0; i < s.length(); i++){
            if (s[i] >= 'a' && s[i] <= 'z'){
                int st = s[i] - 'a';
                i++;
                while (i < s.length() && (s[i] == ' ' || s[i] == '<')) 
					i++;
                if (i < s.length() && s[i] >= 'a' && s[i] <= 'z'){
                    int ed = s[i] - 'a';
                    d[st][ed] = 1;
                    indeg[ed]++;
                }
            }
        }
        dfs(0);
    }
}

其实,和板子没啥区别......

OpenJudge 是真的效率!快到飞起来!

相关推荐
姓蔡小朋友3 小时前
算法-滑动窗口
算法
君义_noip3 小时前
信息学奥赛一本通 2134:【25CSPS提高组】道路修复 | 洛谷 P14362 [CSP-S 2025] 道路修复
c++·算法·图论·信息学奥赛·csp-s
kaikaile19954 小时前
基于拥挤距离的多目标粒子群优化算法(MO-PSO-CD)详解
数据结构·算法
liulilittle4 小时前
OPENPPP2 Code Analysis One
网络·c++·网络协议·信息与通信·通信
不忘不弃4 小时前
求两组数的平均值
数据结构·算法
leaves falling4 小时前
迭代实现 斐波那契数列
数据结构·算法
珂朵莉MM4 小时前
全球校园人工智能算法精英大赛-产业命题赛-算法巅峰赛 2025年度画像
java·人工智能·算法·机器人
Morwit4 小时前
*【力扣hot100】 647. 回文子串
c++·算法·leetcode
AI视觉网奇4 小时前
audio2face mh_arkit_mapping_pose_A2F 不兼容
笔记·ue5
天赐学c语言4 小时前
1.7 - 删除排序链表中的重要元素II && 哈希冲突常用解决冲突方法
数据结构·c++·链表·哈希算法·leecode