2026-01-06 hetao1733837 的刷题笔记
LGP1347 排序
原题链接:排序
分析
虽然......但是......怎么写?
显然套一个数据结构是对的。
等一下,我思考一下如果已经联通......似乎并不见得可以完全确定吧......
居然写过吗?
HT 给了一个极其巧妙的方法:二分答案!
观察发现,有解,必然存在某一个点,其之后都有解,二分出来这个点即可。
妙哉妙哉!甚至把拓扑排序隐藏了!
然后 check \operatorname{check} check 拿一个传递闭包就行。 n ≤ 30 n\le 30 n≤30,我记得是这样......懒得查了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 30;
int n, m;
char c1, c2, c3;
vector<pair<int, int>> e, a;
int d[N][N];
int check(int x){
memset(d, 0, sizeof(d));
for (int i = 0; i < x; i++){
d[e[i].first][e[i].second] = 1;
}
for (int k = 1; k <= n; k++){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
d[i][j] |= (d[i][k] & d[k][j]);
}
}
}
int sum = 0;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
sum += d[i][j];
if (d[i][j] & d[j][i])
return 1;
}
}
if (sum == n * (n - 1) / 2)
return 2;
return 0;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
cin >> c1 >> c2 >> c3;
e.push_back({c1 - 'A' + 1, c3 - 'A' + 1});
}
int l = 1, r = m;
while (l <= r){
int mid = (l + r) >> 1;
if (check(mid) >= 1)
r = mid - 1;
else
l = mid + 1;
}
if (l > m){
cout << "Sorted sequence cannot be determined.";
return 0;
}
int tmp = check(l);
if (tmp == 1){
cout << "Inconsistency found after " << l << " relations.";
return 0;
}
cout << "Sorted sequence determined after " << l << " relations: ";
for (int i = 1; i <= n; i++){
int sum = 0;
for (int j = 1; j <= n; j++){
sum += d[i][j];
}
a.push_back({-sum, i});
}
sort(a.begin(), a.end());
for (auto tmp : a)
cout << (char)(tmp.second - 1 + 'A');
cout << ".";
}
发现板子没写......
我到底在干什么?
LGB3644【模板】拓扑排序 / 家谱树
原题链接:【模板】拓扑排序 / 家谱树
分析
很显然吧......按照《算法竞赛》上的 BFS 思路......因为我不会 DFS 求......好像也会吧......
好像 24 年暑假就打这题了......当时没过......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, a, d[N];
vector<int> e[N];
void topo(){
queue<int> q;
for (int i = 1; i <= n; i++){
if (d[i] == 0){
cout << i << " ";
q.push(i);
}
}
while (!q.empty()){
int u = q.front();
q.pop();
for (auto v : e[u]){
d[v]--;
if (d[v] == 0){
cout << v << " ";
q.push(v);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int idx = 1;
while (idx <= n){
while (1){
cin >> a;
if (a == 0)
break;
e[idx].push_back(a);
d[a]++;
}
idx++;
}
topo();
}
LGP1685 游览
原题链接:游览
分析
有自环......炸了炸了,没看懂题!
那我走自环岂不是可以无限?
非也非也!这个是说可以组成无向边!
那......感觉上来说就是给定起点和终点求起点到终点的路径数,可能套一个和......
好像会了......吗?
拓扑排序+ DP?还是人类吗?
那就是两个,一个记录路径数量,一个记录花费,最后再加乘船的部分。拓扑排序主要主要是小优化。
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 10000
using namespace std;
const int N = 10005;
int n, m, s, t, t0;
vector<pair<int, int>> e[N];
int f[N], g[N]; //分别表示以i为终点的花费和方案数
int d[N];
void dfs(int u){
for (auto tmp : e[u]){
int v = tmp.first, w = tmp.second;
g[v] = (g[v] + g[u]) % mod;
f[v] = (f[v] + f[u] + g[u] * w) % mod;
d[v]--;
if (!d[v])
dfs(v);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> s >> t >> t0;
for (int i = 1, u, v, w; i <= m; i++){
cin >> u >> v >> w;
e[u].push_back({v, w});
d[v]++;
}
g[s] = 1;
dfs(s);
cout << ((g[t] - 1) * t0 + f[t]) % mod;
}
手上起倒刺了,晚上提醒我吃青菜......
LGP3243 [HNOI2015] 菜肴制作
原题链接:[HNOI2015] 菜肴制作
分析
怎么写呢?显然存在一个拓扑序,同时,要求在符合要求的前提下,坏了,语言功能有点小退化......
就是题目那个要求!
注意到这个东西不是完全的字典序,考虑在反图上跑最大,呃......好像就是这么回事,套一个 priority_queue,没了......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int T;
int n, m, d[N], topo[N];
vector<int> e[N];
priority_queue<int> q;
int tot;
void toposort(){
while (!q.empty()){
q.pop();
}
for (int i = 1; i <= n; i++){
if (d[i] == 0){
q.push(i);
}
}
while (!q.empty()){
int u = q.top();
q.pop();
topo[++tot] = u;
for (auto v : e[u]){
d[v]--;
if (d[v] == 0){
q.push(v);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--){
for (int i = 1; i <= n; i++){
e[i].clear();
d[i] = 0;
topo[i] = 0;
}
tot = 0;
cin >> n >> m;
bool flag = true;
for (int i = 1, u, v; i <= m; i++){
cin >> u >> v;
if (u == v)
flag = false;
e[v].push_back(u);
d[u]++;
}
if (!flag){
cout << "Impossible!" << '\n';
continue;
}
toposort();
if (tot != n){
cout << "Impossible!" << '\n';
continue;
}
for (int i = tot; i >= 1; i--){
cout << topo[i] << " ";
}
cout << '\n';
}
}
啥搭配啊?黄绿紫?没有蓝题?
LGP1983 [NOIP 2013 普及组] 车站分级
原题链接:[NOIP 2013 普及组] 车站分级
分析
这个咋建图?
我无法实现建图。
我好像可以实现建图,这个是 O ( n 2 ) O(n^2) O(n2) 的,可以跑下的!就是,我不停的,肯定是小于的对吧!
然后,我不管没有覆盖到的位置,然后呢?
这个是拓扑序的什么?他要求的是最小的划分的级别数。
所以呢?哦,求等级最高的就行。
然后,由于 n ≤ 1 e 3 n\le 1e3 n≤1e3,拿个邻接矩阵反而常数小一点。
那么,要做的就是一层一层剥掉多余的边......我也不太懂......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m, s, e[N][N];
int topo[N];
int num[N], d[N];
bool vis[N], buc[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++){
memset(buc, 0, sizeof(buc));
cin >> s;
for (int j = 1; j <= s; j++){
cin >> num[j];
buc[num[j]] = 1;
}
for (int j = num[1]; j <= num[s]; j++){
if (!buc[j]){
for (int k = 1; k <= s; k++){
if (!e[j][num[k]]){
e[j][num[k]] = 1;
d[num[k]]++;
}
}
}
}
}
int top, ans = 0;
do{
top = 0;
for (int i = 1; i <= n; i++){
if (d[i] == 0 && !vis[i]){
topo[++top] = i;
vis[i] = true;
}
}
for (int i = 1; i <= top; i++){
for (int j = 1; j <= n; j++){
if (e[topo[i]][j]){
e[topo[i]][j] = 0;
d[j]--;
}
}
}
ans++;
}while (top);
cout << ans - 1;
}
显然给放了 O ( n 2 m ) O(n^2m) O(n2m),和我估计的一样......
LGP4934 礼物
原题链接:礼物
分析
真的牛大了,这题直接把拓扑排序引向了处理所有具有大于、小于、大于等于、小于等于关系的问题的解决思路。
数论还在追我/ll
怎么建图?还是 O ( n 2 ) O(n^2) O(n2) 吗?太扯了。
那咋办?瞅一眼题解吧......
好累啊😩好饿啊😣
把事情看作图的点,把先后关系看作有向边,问题转化为图中一个有先后关系的排序,这就是拓扑排序。
对于题目要求 a & b ≥ min ( a , b ) a\&b\ge\min(a,b) a&b≥min(a,b),则 a a a 二进制下 1 1 1 所在的数位与 b b b 二进制下 1 1 1 所在的数位存在严格包含关系。
然后,我们设计出一棵决策树来优化建图。
还是不太明白吧......感觉被掏空了......
感觉理解起来还行吧......虽然我不会......
严肃决定明天再写......
2026年1月7日12点57分
论住宿生晚上回家打 CF \operatorname{CF} CF 的爽感。
那么,继续。哦,显然可以理解吧......就是说,我们按照发现的那个规律去建图,然后,因为题目很人性地给了一个 k k k,所以,直接枚举所有数,第一层是 0 0 0,第二层是所有 2 2 2 的整数次幂,然后依次增加二进制上 1 1 1 的个数。然后,每一层单开一个盒子,不断继承,似乎没了......
呃......大概就这样吧......我没太看明白他树是在哪,先看代码,写写吧......哦,爽了,下午第一节地理不用去上!
lz 去那外卖,我在机房唱歌好像被听见了,ԾㅂԾ,
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005, M = (1 << 20) + 5;
int n, k, a[N], top;
struct node{
int id, p;
}ans[N];
bool cmp(node x, node y){
return x.p < y.p;
}
int tot, cnt[M], pre[M], d[M];
bool vis[M], buc[M];
vector<int> e[M];
queue<int> q;
void build(){
while (!q.empty()){
q.pop();
}
q.push(0);
while (!q.empty()){
int u = q.front();
q.pop();
for (int i = 0; i < k; i++){
if (((u >> i) & 1) == 0){
int v = u | (1 << i);
e[u].push_back(v);
d[v]++;
if (!vis[v]){
q.push(v);
vis[v] = 1;
}
}
}
}
}
void toposort(){
while (!q.empty()){
q.pop();
}
q.push(0);
while (!q.empty()){
int u = q.front();
q.pop();
if (buc[u]){
pre[u]++;
ans[++top] = {u, pre[u]};
cnt[pre[u]]++;
}
tot = max(tot, pre[u]);
for (auto v : e[u]){
d[v]--;
pre[v] = max(pre[v], pre[u]);
if (d[v] == 0){
q.push(v);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++){
cin >> a[i];
buc[a[i]] = 1;
}
build();
toposort();
sort(ans + 1, ans + n + 1, cmp);
cout << 1 << '\n';
cout << tot << '\n';
for (int i = 1; i <= n; i++){
if (ans[i].p != ans[i - 1].p){
cout << cnt[ans[i].p] << " ";
}
cout << ans[i].id << " ";
if (ans[i].p != ans[i + 1].p){
cout << '\n';
}
}
}