寒假2024/2/17
想开学了
强连通分量模板
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
vector<int>e[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top; // 栈的作用是记录正在访问的点
int scc[N], siz[N], cnt; //最后的结果是scc[i] = k 记录第i个节点在第k个强连通分量里,大小是siz【k】
void tarjan(int x) {
//入x时, 盖戳, 入栈
dfn[x] = low[x] = ++tot;
stk[++top] = x, instk[x] = 1;
for (int y : e[x]) {
if(!dfn[y]) { //若y尚未访问
tarjan(y);
low[x] = min(low[x], low[y]); //回x时更新low
}
else if(instk[y]) { //若y已访问且在栈中
low[x] = min(low[x], low[y]);
}
// 剩下已经访问并且不在栈中的情况是y已经被访问完,不在枚举
}
//离x时,记录scc
if(dfn[x] == low[x]) { //若x是scc的根
int y; ++cnt;
do{ //陆续把强连通分量出栈
y = stk[top--]; instk[y] = 0;
scc[y] = cnt; //scc编号
++siz[cnt]; //scc大小
} while(y != x);
}
}
uva297
题意:
给四分图,32 x 32,图是递归形式给出的,要求求出两个四分图合并后的新四分图的黑色块数。
思路:
起初我没弄懂给的样例448 640那些数字是怎么来的,后来才发现题目给的一块是好几个色素块(我是本币)
弄懂题意我们就可以想思路了,起初我看题目引导我们建树,我开始建立四叉树,但是越写越别扭,又要建树,又要合并。
所以我换了一种思路:直接填图,填两遍图,最后统计数量就行了。
(1)起初我们先给图赋值0表示全白。
(2)然后我们按顺序对两个字符串进行操作,如果全白不管,黑就涂黑,灰色就递归再涂。
但是我们要想怎么用递归跳转位置填图呢?
我看了dalao写的题解,很妙x, y, w三个参数位置和边长就能知道目标正方形了。
(3)最后我们统计数量乘以16就是答案。
最后有一点小bug,题目给的正方形是缩放的,样例给的是32 x 32的
#include <bits/stdc++.h>
using namespace std;
#define int long long
int arr[35][35] = {0};
int loc = -1;
string s;
void draw(int x, int y, int w) {
++loc;
// cerr << x << " " << y << " " << w << " " << s[loc] << endl;
if(s[loc] == 'f') {
for (int i = x; i < x + w; i++) {
for (int j = y; j > y - w; j--) {
arr[i][j] = 1;
}
}
return;
}
else if(s[loc] == 'p') {
draw(x, y, w / 2);
draw(x, y - w / 2, w / 2);
draw(x + w / 2, y - w / 2, w / 2);
draw(x + w / 2, y, w / 2);
}
}
void print() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}
void solve() {
memset(arr, 0, sizeof(arr));
for (int i = 0; i < 2; i++) {
cin >> s;
loc = -1;
draw(0, 31, 32);
// print();
}
int ans = 0;
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 32; j++) {
if(arr[i][j]) {
ans++;
}
}
}
cout << "There are " << ans << " black pixels.\n";
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
cin >> T;
while(T--) {
solve();
}
return 0;
}
uva247
题意:
给n个人,m条通话记录,如果一群人能互相打给对方,就是一个团体,把每个团体输出。
思路:
这个题实质就是强连通分量,要加一点字符串的哈希。
刚学的,套模板,嘎嘎快。
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5;
vector<int>e[N];
int dfn[N], low[N], tot;
int stk[N], instk[N], top; // 栈的作用是记录正在访问的点
int scc[N], siz[N], cnt; //最后的结果是scc[i] = k 记录第i个节点在第k个强连通分量里,大小是siz【k】
vector<string> name;
map<string, int>mp;
void tarjan(int x) {
//入x时, 盖戳, 入栈
dfn[x] = low[x] = ++tot;
stk[++top] = x, instk[x] = 1;
for (int y : e[x]) {
if(!dfn[y]) { //若y尚未访问
tarjan(y);
low[x] = min(low[x], low[y]); //回x时更新low
}
else if(instk[y]) { //若y已访问且在栈中
low[x] = min(low[x], low[y]);
}
// 剩下已经访问并且不在栈中的情况是y已经被访问完,不在枚举
}
//离x时,记录scc
if(dfn[x] == low[x]) { //若x是scc的根
int y; ++cnt;
bool flag = 0;
do{ //陆续把强连通分量出栈
y = stk[top--]; instk[y] = 0;
scc[y] = cnt; //scc编号
if(flag) {
cout << ", ";
}
else {
flag = 1;
}
cout << name[y - 1];
++siz[cnt]; //scc大小
} while(y != x);
cout << endl;
}
}
void solve(int n, int m) {
tot = 0;
top = 0;
cnt = 0;
for (int i = 1; i <= n; i++) {
dfn[i] = 0;
low[i] = 0;
stk[i] = 0;
instk[i] = 0;
e[i].clear();
}
name.clear();
mp.clear();
for (int i = 0; i < m; i++) {
string s, t;
cin >> s >> t;
if(mp.count(s) == 0) {
name.push_back(s);
mp[s] = name.size();
}
if(mp.count(t) == 0) {
name.push_back(t);
mp[t] = name.size();
}
int a = mp[s], b = mp[t];
e[a].push_back(b);
}
for (int i = 1; i <= n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
int cnt = 1;
while(cin >> n >> m && n) {
cout << "Calling circles for data set " << cnt++ << ":" << endl;
solve(n, m);
}
return 0;
}