2026-01-09~12 hetao1733837 的刷题笔记
01-09
孩子们,博主以后不只是 OIer 了,还是 BOer 了!
挺梦幻的,说实话。很早以前我就幻想过双竞甚至多竞,没想到有朝一日真的可能实现!
但是,话又说回来,此时最重要的是低调!太过张扬只会害了你 fbh!
刷题!双竞是都要兼顾,为升学提供双重保障,文化课必须不能落!我们也需要给出 BO 生涯的一些规划......同时,我们的 OI 也不是很顶级,所以,必须平衡⚖并最大化收益!
POJ1144 Network
原题链接:Network
分析
割点小板子......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int low[N], dfn[N], ncnt;
bool buc[N];
vector<int> e[N];
void dfs(int u, int pre){
low[u] = dfn[u] = ++ncnt;
int son = 0;
for (auto v : e[u]){
if (!dfn[v]){
son++;
dfs(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && u != 1){
buc[u] = true;
}
}
else if (dfn[v] < dfn[u] && v != pre){
low[u] = min(low[u], dfn[v]);
}
}
if (u == 1 && son >= 2)
buc[1] = true;
}
int n, u, v;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
while (1){
scanf("%d", &n);
if (n == 0){
return 0;
}
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(buc, 0, sizeof(buc));
ncnt = 0;
for (int i = 0; i <= n; i++){
e[i].clear();
}
while (scanf("%d", &u) && u){
while (getchar() != '\n'){
scanf("%d", &v);
e[u].push_back(v);
e[v].push_back(u);
}
}
int ans = 0;
dfs(1, 1);
for (int i = 1; i <= n; i++)
ans += buc[i];
cout << ans << '\n';
}
}
01-12
LGP7073 [CSP-J 2020] 表达式
原题链接:[CSP-J 2020] 表达式
分析
对于一个表达式,有些位置对答案是没有贡献的,比如 0 0 0 与某个数按位与, 1 1 1 与某个数按位或,这个数都是没有用的,那么标记一下,先求出整体的答案,然后看有没有被标记即可。
对于非运算,把之后的都取反即可。
表达式用一个栈模拟出来即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
string s;
int n, inp[N], q, pos;
int son[N][2];
int buc[N];
int tag[N];
int dfs1(int u, int val){
inp[u] ^= val;
if (u <= n) {
return inp[u];
}
int a = dfs1(son[u][0], val ^ buc[son[u][0]]);
int b = dfs1(son[u][1], val ^ buc[son[u][1]]);
if (inp[u] == 2){
if (a == 0){
tag[son[u][1]] = 1;
}
if (b == 0){
tag[son[u][0]] = 1;
}
return a & b;
}
else{
if (a == 1){
tag[son[u][1]] = 1;
}
if (b == 1){
tag[son[u][0]] = 1;
}
return a | b;
}
}
void dfs2(int u){
if (u <= n){
return ;
}
tag[son[u][0]] |= tag[u];
tag[son[u][1]] |= tag[u];
dfs2(son[u][0]);
dfs2(son[u][1]);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
getline(cin, s);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> inp[i];
}
stack<int> stk;
int cnt = n;
for (int i = 0; i < (int)s.length(); i++){
if (s[i] == ' ')
continue;
if (s[i] == 'x'){
int tmp = 0;
i++;
while (i < s.length() && s[i] >= '0' && s[i] <= '9'){
tmp = tmp * 10 + (s[i] - '0');
i++;
}
i--;
stk.push(tmp);
}
else if (s[i] == '&'){
int tmp2 = stk.top(); stk.pop();
int tmp1 = stk.top(); stk.pop();
stk.push(++cnt);
inp[cnt] = 2;
son[cnt][0] = tmp1;
son[cnt][1] = tmp2;
}
else if (s[i] == '|'){
int tmp2 = stk.top(); stk.pop();
int tmp1 = stk.top(); stk.pop();
stk.push(++cnt);
inp[cnt] = 3;
son[cnt][0] = tmp1;
son[cnt][1] = tmp2;
}
else if (s[i] == '!'){
buc[stk.top()] ^= 1;
}
}
int rt = cnt;
int ans = dfs1(rt, buc[rt]);
dfs2(rt);
cin >> q;
while (q--){
cin >> pos;
if (tag[pos])
cout << ans << '\n';
else
cout << !ans << '\n';
}
return 0;
}
HDU1296 迷宫城堡
原题链接:迷宫城堡
分析
强连通分量......不多说......
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
vector<int> e[N];
int scc[N], cnt, stk[N], top;
int low[N], dfn[N], ncnt;
void dfs(int u){
stk[top++] = u;
low[u] = dfn[u] = ++ncnt;
for (auto v : e[u]){
if (!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (!scc[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]){
cnt++;
while (true){
int v = stk[--top];
scc[v] = cnt;
if (u == v)
break;
}
}
}
void tarjan(int n){
cnt = top = ncnt = 0;
memset(scc, 0, sizeof(scc));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
for (int i = 1; i <= n; i++){
if (!dfn[i])
dfs(i);
}
}
int n, m;
signed main(){
while (scanf("%d %d", &n, &m), n != 0 || m != 0){
for (int i = 0; i <= n; i++){
e[i].clear();
}
for (int i = 1, u, v; i <= m; i++){
cin >> u >> v;
e[u].push_back(v);
}
tarjan(n);
if (cnt == 1)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
}
}
千万记住,关闭流输入后不要 scanf,getchar 这些东西混用!
LGP2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G
原题链接:[USACO03FALL / HAOI2006] 受欢迎的牛 G
分析
没看懂题解......
那不是要求有且只有一个强连通分量吗?这是对的,那么出度为 0 的点即为明星,这里有且只有一个,如果多个出度为 0 的点,显然不满足。建一个反图更加合适。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int n, m;
vector<int> e[N];
int cnt, low[N], dfn[N], scc[N], sz[N], d[N];
int stk[N], tp, ncnt;
bool vis[N];
void dfs(int u) {
stk[++tp] = u;
vis[u] = true;
low[u] = dfn[u] = ++ncnt;
for (auto v : e[u]){
if (!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (vis[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]){
cnt++;
int v;
do{
v = stk[tp--];
vis[v] = false;
scc[v] = cnt;
sz[cnt]++;
}while (u != v);
}
}
void tarjan(){
for (int i = 1; i <= n; i++){
if (!dfn[i]){
dfs(i);
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1, u, v; i <= m; i++){
cin >> u >> v;
e[u].push_back(v);
}
tarjan();
for (int u = 1; u <= n; u++){
for (auto v : e[u]){
if (scc[u] != scc[v]){
d[scc[v]]++;
}
}
}
int dcnt = 0, ans = 0;
for (int i = 1; i <= cnt; i++){
if (d[i] == 0){
dcnt++;
ans = sz[i];
}
}
if (dcnt == 1){
cout << ans;
}
else{
cout << 0;
}
}
LGP1407 [国家集训队] 稳定婚姻
原题链接:[国家集训队] 稳定婚姻
分析
可不可以理解为,似乎只剩下有向图了......
那咋办啊?和强连通分量的个数关系似乎有一点关系啊。
两个?相当于所有都一样吗?你删了一条边,咋会还一样呢?万一呢?那当我没说好吧......
那么,开始吧......
呃......无向边似乎是为了使得整个图保持联通,怎么搞啊?我好像不太会啊。
正确思路
我们对于夫妻建边,呃......前 npy \operatorname{npy} npy 也建边,当一对夫妻在一个环内时,则婚姻不安全。
这个时候可以跑 Tarjan,但是,Tarjan 适用于有向图,所以,建边时要 ⋯ → \dots\rightarrow ⋯→ 女 → \rightarrow → 男 → \rightarrow → 女 → ... \rightarrow\dots →...,这样建边就可以了。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 20005;
map<string, int> mp;
vector<int> e[N];
int low[N], dfn[N], ncnt, circl[N];
int stk[N], tp, cnt;
bool vis[N];
void dfs(int u){
low[u] = dfn[u] = ++ncnt;
stk[++tp] = u;
vis[u] = true;
for (auto v : e[u]){
if (!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (vis[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]){
cnt++;
do{
circl[stk[tp]] = cnt;
vis[stk[tp]] = 0;
}while (stk[tp--] != u);
}
}
void tarjan(int n){
for (int i = 1; i <= n; i++){
if (!dfn[i]){
dfs(i);
}
}
}
int n, m;
string g, b;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> g >> b;
mp[g] = i;
mp[b] = n + i;
e[i].push_back(n + i);
}
cin >> m;
for (int i = 1; i <= m; i++){
cin >> g >> b;
e[mp[b]].push_back(mp[g]);
}
tarjan(n * 2);
for (int i = 1; i <= n; i++){
if (circl[i] == circl[i + n]){
cout << "Unsafe" << '\n';
}
else{
cout << "Safe" << '\n';
}
}
}
LGP2272 [ZJOI2007] 最大半连通子图
原题链接:[ZJOI2007] 最大半连通子图
分析
妈呀,上题太猎奇了,堪比那个上厕所的......感觉再刷几道可以组一个很猎奇的场了。
感觉这个可以 hxf 容斥......
还是手模样例吧......
强连通显然是极其严格的半联通吧......那我们求出最大的强连通分量之后,直接扩展不就行了?在扩展的过程中似乎可以做......
没模出来......
回去上了一节生竞课,感觉要背的蛮多的......
好的,继续整这道题......我觉得我得做好规划。现在每天的容量相当大啊......
居然是个 DP!
记 f f f 为最大半连通子图的大小, g g g 为方案数。在拓扑序上 DP 。
这里有两个 Trick ,
1)Tarjan缩点后点的排序是逆拓扑序的
2)拓扑序 DP 下,判断重边只需记录上一个从哪转移即可
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, m, x;
int low[N], dfn[N], ncnt, belong[N];
vector<int> e[N], g[N];
int f1[N], f2[N];
int stk[N], tp;
bool instk[N];
int cnt;
int sz[N], lst[N];
void dfs(int u){
low[u] = dfn[u] = ++ncnt;
stk[++tp] = u;
instk[u] = true;
for (auto v : e[u]){
if (!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (instk[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]){
cnt++;
int k;
do{
k = stk[tp--];
belong[k] = cnt;
sz[cnt]++;
instk[k] = false;
}while(k != u);
}
}
void tarjan(int n){
for (int i = 1; i <= n; i++){
if (!dfn[i]){
dfs(i);
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> x;
for (int i = 1, u, v; i <= m; i++){
cin >> u >> v;
e[u].push_back(v);
}
tarjan(n);
for (int u = 1; u <= n; u++){
for (auto v : e[u]){
if (belong[u] == belong[v])
continue;
g[belong[u]].push_back(belong[v]);
}
}
for (int i = 1; i <= cnt; i++){
f1[i] = sz[i];
f2[i] = 1;
}
for (int u = cnt; u >= 1; u--){
sort(g[u].begin(), g[u].end());
g[u].erase(unique(g[u].begin(), g[u].end()), g[u].end());
for (auto v : g[u]){
if (f1[v] < f1[u] + sz[v]){
f1[v] = f1[u] + sz[v];
f2[v] = f2[u];
}
else if (f1[v] == f1[u] + sz[v]){
f2[v] = (f2[v] + f2[u]) % x;
}
}
}
int maxn = 0, ans = 0;
for (int i = 1; i <= cnt; i++){
if (f1[i] > maxn){
maxn = f1[i];
ans = f2[i];
}
else if (f1[i] == maxn){
ans = (ans + f2[i]) % x;
}
}
cout << maxn << '\n';
cout << ans;
return 0;
}
严肃思考要不要写文化作业......
觉得实在有点学不动了......静下来......那么,让我们进入与 DP 搏斗吧!