学术社区
学习了 出题人题解
题意
\(n\) 个人,重排 \(m\) 个消息,使得符合实际的消息最多。
每个消息有 \(s_1, s_2, s_3\) 三个字符串。
-
楼下型消息:对于消息 \(i\), 满足 \(s_3 = \text{louxia}\),\(s_{i, 2} = s_{i - 1, 1}\)。
-
楼上型消息:对于消息 \(i\), 满足 \(s_3 = \text{loushang}\), \(s_{i, 2} = s_{i + 1, 1}\)。
每一个在帖子中发过言的人都一定会在帖子中发出至少一条学术消息。
特殊性质 ABC
特殊性质 A: 很好理解,只用考虑楼下型消息。
现在有只有 学术消息 和 楼下型消息。
对于楼下型消息 \((A, B, \text{louxia})\),能发现一个明显的指向关系。
对于多个楼下型消息,就是一串相连的关系。
因此,考虑把这些关系抽象成有向图,每一条关系可以将图上 \(B\) 连向 \(A\)。
经过一条边的意义是选择这个消息,停留在某点的意义就是当前最后说话的人,楼下型消息连边的意义就是现在 \(A\) 可以说话,下一个说话的人是 \(B\), 才能选择当前消息。
特殊性质 B: 每条楼下型消息得用。
求题目要求的最多消息个数,这里显然就是要把图中每条边都走一遍。
对于重排后的答案的样子,应该是:
- 学术消息 -> 楼下 -> 楼下 ... 学术消息 -> 楼下 ...
问题可以转化为,在图上任选一条路径 \((u, v)\), 并且 \(u\) 点前还要有学术消息,在 \(v\) 时停止,最后要把所有边覆盖。
欧拉回路
覆盖所有边,可以考虑 欧拉回路 的算法。
- 合法的欧拉回路要满足: 任意点的 入度等于出度。
-
首先对于学术消息 \((A, B,C)\)。
建虚点 \(T\), 并且 \(T\) 向 \(A\) 连一条边,表示当前最后说话的是 \(A\)。
-
实际上,每个点的 入度大于等于出度。
这里出度可以理解为有多少条楼下消息,而入度理解为出度中的楼下消息可以选择的楼上,并且有这些能选。
性质B保证每个消息都有的选楼上。
-
对于点 \(A\), 出度小于入度,就连 \(A\) 到 \(T\) 的边,直到出度等于入度。
最后得到的路径方案就是合法方案。
随手画了个图:
两条红边就是加入的虚边。
可以发现选出来的,都是 \(T \to A \to B ... \to T \to C ... \to T\) 这样的回路。
特殊性质 AC
和 特殊性质ABC 的分析一样,但是没有保证楼下型消息都有用。
在图中出现的情况是:存在点 \(A\) 的 入度小于出度。
按照上面的理解:
- 入度理解为出度中的楼下消息可以选择的楼上。
那么就会有楼上型消息选不到它要求的楼上。
为了满足欧拉回路的要求,那么就从 \(T\) 向 \(A\) 连边,直到入度等于出度。
对于答案中的一段回路, 例如: \(... \to T \to A \to B \to T \to ...\)。
如果 \(T \to A\) 是补充的虚边,那么 \(A \to B\) 这条关系就不满足,因为 \(A\) 并未发言。
图:
如图, \(T \to D\) 是补充的虚边, 而 \(D \to C\) 的不符合实际。
特殊性质 BC
这时加入了楼上型消息。
对于一条楼上型消息 \((A, B, \text{loushang})\), 此时应该将 \(A\) 连向 \(B\)。
因为和楼下型消息连边的意义差不多,当前 \(A\) 可以说话,只有下一个人是 \(B\) 这个消息才合法。
用一个图难以表示两种不同的类型,考虑分层图。
对于楼上型消息的图,称之为 \(G_0\), 对于楼下型消息的图,称之为 \(G_1\)。
-
楼下型消息 \((A, B, \text{louxia})\), 在 \(G_0\) 连 \(B \to A\)。
-
楼上型消息 \((A, B, \text{loushang})\), 在 \(G_1\) 连 \(A \to B\)。
这时的学术消息可以在楼上型和楼下型消息之间做桥梁连接。
- 学术消息 \((A, B,C)\),在 \(G_1\) 的 \(A\) 连向 \(G_0\) 的 \(A\),简称 \(A_1 \to A_0\)。
特殊性质 B 保证:
- 在 \(G_0\) 中 入度大于等于出度。
- 在 \(G_1\) 中 出度大于等于入度。
\(G_0\) 中, \(A\) 连 入度减出度 条 \(A -> T\)。
\(G_1\) 中, \(A\) 连 出度减入度 条 \(T -> A\)。
这样就可以跑欧拉回路了。
红边是补充边,蓝边是学术消息。
可以发现路径是 \(T \to A_1 \to B_1 \to ... \to C_1 \to A_0 \to B_0 \to T \to ...\)
特殊性质 C
少了特殊性质 B, 就会有楼上型消息或楼下型消息配不上它要的楼上或楼下。
- 在 \(G_0\) 中表现为,存在点 \(A\) 的 入度小于出度。
- 在 \(G_1\) 中表现为,存在点 \(A\) 的 入度大于出度。
那么还不如当成学术消息利用,问题是怎么选出那些要变成学术消息?
-
在 \(G_0\) 中,如果存在点 \(A\) 的入度小于出度,那么有 出度-入度 条边用不上。
假设边是楼下型消息 \(B_0 \to A_0\), 那么改成学术消息后就是 \(A_1 \to A_0\)。
这里的变化是 \(B_0\) 的出度减小 \(1\), \(A_1\) 的出度增加 \(1\)。
对于点 \(B_0\) 和 \(A_1\) 用不到的消息都各自减少 \(1\)。
-
对于 \(G_1\) 也同理:
假设边是楼上型消息 \(A_1 \to B_1\), 那么改成学术消息后就是 \(A_1 \to A_0\)。
这里的变化是 \(B_1\) 的入度减小 \(1\), \(A_0\) 的入度增加 \(1\)。
对于点 \(A_0\) 和 \(B_1\) 用不到的消息都各自减少 \(1\)。
这就可以表示成一个网络流模型:
-
\(G_0\) 中,如果存在点 \(A\) 的入度小于出度,那么 \(A_0 \to T\), 流量是 \(\deg_{out}(A_0) - \deg_{in}(A_0)\)。
-
\(G_1\) 中,如果存在点 \(A\) 的入度大于出度,那么 \(S \to A_1\), 流量是 \(\deg_{in}(A_1) - \deg_{out}(A_1)\)。
-
对于楼下型消息 \((A, B, \text{louxia})\), \(A_1 \to B_0\), 流量是 \(1\)。
-
对于楼上型消息 \((A, B, \text{loushang})\), \(B_1 \to A_0\), 流量是 \(1\)。
求最大流,网络流中剩余流量为 \(0\)的边表示的楼下楼上型消息,会变成学术消息。
但是仍然可能出现 \(G_0\) 入度小于出度的情况,就像特殊性质 AC 一样解决即可。
正解
发现特殊性质 C 没了后,楼上型消息 \((A, B, \text{louxia})\), 其实可以和楼下型消息 \((B, A, \text{louxia})\) 组合在一起, 这样总是不劣的。
这样的组合也能像学术消息一样在楼上型消息和楼下型消息之间。
可以在图中连 \(A_1 \to B_0\)。
这些组合就可以不用考虑了。
采用 特殊性质 C 的解法即可。
代码
每个子任务的代码都有。
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int MAXN = 160010;
template <typename T>
void Read(T &x) {
x = 0; T f = 1; char a = getchar();
for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
x *= f;
}
int n, m;
map<string, int> id;
struct inf {
string s1, s2, s3;
int id, p;
} a[MAXN];
/* 欧拉回路中 -1 表示图中的辅助边红边,- 2 表示辅助边绿边, >0 表示非学术性消息,< -3 表示缩起来的楼上楼下组合*/
/*cnt 表示最大的符合实际的信息*/
namespace ABC {
struct dat {
int v, id;
dat (int x = 0, int y = 0) {
v = x, id = y;
}
};
vector<dat> e[MAXN];
int cur[MAXN];
int ind[MAXN], outd[MAXN];
stack<int> ans;
void dfs(int u, int id) {
for (int i = cur[u]; i < int(e[u].size()); i = cur[u]) {
++ cur[u];
dfs(e[u][i].v, e[u][i].id);
}
if (~id) ans.push(id);
}
void solve() {
int T = m + 1, cnt = 0;
for (int i = 1; i <= n; i ++)
e[i].clear(),
ind[i] = outd[i] = cur[i] = 0;
for (int i = 1; i <= m; i ++) {
if ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end()) { // 学术消息
int v = id[a[i].s1];
e[T].emplace_back(v, i);
outd[T] ++;
ind[v] ++;
} else { // 楼下消息
int u = id[a[i].s1], v = id[a[i].s2];
e[v].emplace_back(u, i); // v -> u
outd[v] ++;
ind[u] ++;
cnt ++;
}
}
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= ind[i] - outd[i]; j ++)
e[i].emplace_back(T, -1);
dfs(T, -1);
cout << cnt << '\n';
// assert(ans.size() == m);
while (!ans.empty())
cout << ans.top() << ' ',
ans.pop();
cout << '\n';
}
}
namespace AC {
struct dat {
int v, id;
dat (int x = 0, int y = 0) {
v = x, id = y;
}
};
vector<dat> e[MAXN];
int cur[MAXN];
int ind[MAXN], outd[MAXN];
stack<int> ans;
int cnt;
void dfs(int u, int id) {
for (int i = cur[u]; i < int(e[u].size()); i = cur[u]) {
++ cur[u];
dfs(e[u][i].v, e[u][i].id);
}
if (id == -2) cnt --;
if (id > 0) ans.push(id);
}
void solve() {
int T = m + 1;
cnt = 0;
for (int i = 1; i <= n; i ++)
e[i].clear(),
ind[i] = outd[i] = cur[i] = 0;
for (int i = 1; i <= m; i ++) {
if ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end()) { // 学术消息
int v = id[a[i].s1];
e[T].emplace_back(v, i);
outd[T] ++;
ind[v] ++;
} else { // 楼下消息
int u = id[a[i].s1], v = id[a[i].s2];
e[v].emplace_back(u, i); // v -> u
outd[v] ++;
ind[u] ++;
cnt ++;
}
}
for (int i = 1; i <= n; i ++) {
if (ind[i] > outd[i])
for (int j = 1; j <= ind[i] - outd[i]; j ++)
e[i].emplace_back(T, -1);
if (ind[i] < outd[i])
for (int j = 1; j <= outd[i] - ind[i]; j ++)
e[T].emplace_back(i, -2);
}
dfs(T, -1);
cout << cnt << '\n';
// assert(ans.size() == m);
while (!ans.empty()) {
cout << ans.top() << ' ';
ans.pop();
}
cout << '\n';
}
}
namespace BC {
struct dat {
int v, id;
dat (int x = 0, int y = 0) {
v = x, id = y;
}
};
vector<dat> e[MAXN];
int cur[MAXN];
int ind[MAXN], outd[MAXN];
stack<int> ans;
void dfs(int u, int id) {
for (int i = cur[u]; i < int(e[u].size()); i = cur[u]) {
++ cur[u];
dfs(e[u][i].v, e[u][i].id);
}
if (~id) ans.push(id);
}
int getid(int x, int t) {
return x + t * n;
}
void solve() {
int T = n * 2 + 1, cnt = 0;
for (int i = 1; i <= 2 * n; i ++)
e[i].clear(),
ind[i] = outd[i] = cur[i] = 0;
for (int i = 1; i <= m; i ++) {
if ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end()) { // 学术消息
int x = id[a[i].s1], u = getid(x, 0), v = getid(x, 1);
e[v].emplace_back(u, i);
outd[v] ++;
ind[u] ++;
} else {
int type = (a[i].s3 == "loushang");
int u = getid(id[a[i].s1], type), v = getid(id[a[i].s2], type);
if (type == 0) { // 楼下消息
e[v].emplace_back(u, i); // v -> u
outd[v] ++;
ind[u] ++;
cnt ++;
} else { // 楼上消息
e[u].emplace_back(v, i); // u -> v
outd[u] ++;
ind[v] ++;
cnt ++;
}
}
}
for (int i = 1; i <= n; i ++) {
int u = getid(i, 0);
for (int j = 1; j <= ind[u] - outd[u]; j ++)
e[u].emplace_back(T, -1);
int v = getid(i, 1);
for (int j = 1; j <= outd[v] - ind[v]; j ++)
e[T].emplace_back(v, -1);
}
dfs(T, -1);
cout << cnt << '\n';
// cerr << "?" << ans.size() << ' ' << m << endl;
// assert(ans.size() == m);
while (!ans.empty())
cout << ans.top() << ' ',
ans.pop();
cout << '\n';
}
}
namespace C {
struct Maxflow {
struct edge {
int point, next, len, id;
} e[MAXN];
int cnt = 1;
int first[MAXN];
int cur[MAXN];
void add(int u, int v, int w, int id) {
++ cnt;
e[cnt].point = v;
e[cnt].next = first[u];
e[cnt].len = w;
e[cnt].id = id;
first[u] = cnt;
}
void Add(int u, int v, int w, int id) {
// cerr << u << ' '<< v << endl;
add(u, v, w, id);
add(v, u, 0, 0);
}
int S, T;
int dep[MAXN];
bool bfs() {
for (int i = 1; i <= T; i ++)
dep[i] = -1;
memcpy(cur, first, sizeof(first));
queue<int> q;
q.push(S); dep[S] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
// cerr << "?now:" << u << endl;
for (int i = first[u]; i; i = e[i].next) {
int v = e[i].point;
if (!e[i].len || dep[v] != -1) continue;
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[T] != -1;
}
int dfs(int u, int flow) {
if (u == T)
return flow;
int sum = 0;
for (int &i = cur[u]; i; i = e[i].next) {
int v = e[i].point;
if (!e[i].len || dep[v] != dep[u] + 1) continue;
int out = dfs(v, min(flow, e[i].len));
flow -= out, sum += out;
e[i].len -= out, e[i ^ 1].len += out;
if (!flow) break;
}
return sum;
}
void init(int x, int y) {
S = x, T = y;
cnt = 1;
for (int i = 1; i <= T; i ++)
first[i] = 0;
}
int dinic() {
int maxflow = 0;
while (bfs())
maxflow += dfs(S, 0x3f3f3f3f);
// cerr << "?maxflow:" << maxflow << endl;
return maxflow;
}
} net;
struct dat {
int v, id;
dat (int x = 0, int y = 0) {
v = x, id = y;
}
};
vector<dat> e[MAXN];
int cur[MAXN];
int ind[MAXN], outd[MAXN];
bool vis[MAXN];
stack<int> ans;
int cnt;
void dfs(int u, int id) {
for (int i = cur[u]; i >= 0; i = cur[u]) {
cur[u] --;
dfs(e[u][i].v, e[u][i].id);
}
if (id > 0) ans.push(id), cnt ++;
if (id == -2) cnt --;
}
int getid(int x, int t) {
return x + t * n;
}
void del(int x, int v, int id) {
for (auto i = e[x].begin(); i != e[x].end(); i ++) {
if (i -> v == v && i -> id == id) {
e[x].erase(i);
break;
}
}
}
void solve() {
// cerr << "?Text:" << endl;
int T = n * 2 + 1; cnt = 0;
for (int i = 1; i <= 2 * n + 1; i ++)
e[i].clear(),
ind[i] = outd[i] = cur[i] = 0;
for (int i = 1; i <= m; i ++) {
if ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end()) { // 学术消息
int x = id[a[i].s1], u = getid(x, 0), v = getid(x, 1);
e[v].emplace_back(u, i);
outd[v] ++;
ind[u] ++;
cnt --;
} else {
int type = (a[i].s3 == "loushang");
int u = getid(id[a[i].s1], type), v = getid(id[a[i].s2], type);
if (type == 0) { // 楼下消息
e[v].emplace_back(u, i); // v -> u
outd[v] ++;
ind[u] ++;
} else { // 楼上消息
e[u].emplace_back(v, i); // u -> v
outd[u] ++;
ind[v] ++;
}
}
}
net.init(n * 2 + 1, n * 2 + 2);
for (int i = 1; i <= n; i ++) {
int u = getid(i, 0);
if (ind[u] < outd[u])
net.Add(u, net.T, outd[u] - ind[u], -1);
int v = getid(i, 1);
if (outd[v] < ind[v])
net.Add(net.S, v, ind[v] - outd[v], -1);
}
for (int i = 1; i <= m; i ++)
if (! ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end())) {
if (a[i].s3 == "louxia") { // 楼下消息
int u = getid(id[a[i].s1], 1), v = getid(id[a[i].s2], 0);
net.Add(u, v, 1, i);
} else { // 楼上消息
int u = getid(id[a[i].s2], 1), v = getid(id[a[i].s1], 0);
net.Add(u, v, 1, i);
}
}
net.dinic();
int tot = 0;
for (int i = 2; i <= net.cnt; i ++)
if (net.e[i].id > 0 && net.e[i ^ 1].len) {
// cerr << "?ban:" << net.e[i].id << endl;
int x = id[a[net.e[i].id].s1], y = id[a[net.e[i].id].s2], u = getid(x, 0), v = getid(x, 1);
if (a[net.e[i].id].s3 == "louxia") { // y -> x
x = getid(x, 0), y = getid(y, 0);
del(y, x, net.e[i].id);
outd[y] --;
ind[x] --;
} else { // x -> y
x = getid(x, 1), y = getid(y, 1);
del(x, y, net.e[i].id);
outd[x] --;
ind[y] --;
}
e[v].emplace_back(u, net.e[i].id);
ind[u] ++,
outd[v] ++;
tot ++;
cnt --;
}
// cerr << "!edgecnt:" << tot << endl;
for (int i = 1; i <= n; i ++) {
int u = getid(i, 0);
if (ind[u] > outd[u])
for (int j = 1; j <= ind[u] - outd[u]; j ++)
e[u].emplace_back(T, -1);
if (ind[u] < outd[u])
for (int j = 1; j <= outd[u] - ind[u]; j ++)
e[T].emplace_back(u, -2);
int v = getid(i, 1);
if (outd[v] > ind[v])
for (int j = 1; j <= outd[v] - ind[v]; j ++)
e[T].emplace_back(v, -1);
if (outd[v] < ind[v])
for (int j = 1; j <= ind[v] - outd[v]; j ++)
e[v].emplace_back(T, -2);
}
for (int i = 1; i <= 2 * n + 1; i ++)
cur[i] = int(e[i].size()) - 1;
dfs(T, -1);
cout << cnt << '\n';
// cerr << "?" << ans.size() << ' ' << m << endl;
assert(ans.size() == m);
// cout << ans.size() <<endl;
while (!ans.empty())
cout << ans.top() << ' ',
ans.pop();
cout << '\n';
}
}
namespace solution {
struct Maxflow {
struct edge {
int point, next, len, id;
} e[MAXN];
int cnt = 1;
int first[MAXN];
int cur[MAXN];
void add(int u, int v, int w, int id) {
++ cnt;
e[cnt].point = v;
e[cnt].next = first[u];
e[cnt].len = w;
e[cnt].id = id;
first[u] = cnt;
}
void Add(int u, int v, int w, int id) {
// cerr << u << ' '<< v << endl;
add(u, v, w, id);
add(v, u, 0, 0);
}
int S, T;
int dep[MAXN];
bool bfs() {
for (int i = 1; i <= T; i ++)
dep[i] = -1;
memcpy(cur, first, sizeof(first));
queue<int> q;
q.push(S); dep[S] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
// cerr << "?now:" << u << endl;
for (int i = first[u]; i; i = e[i].next) {
int v = e[i].point;
if (!e[i].len || dep[v] != -1) continue;
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[T] != -1;
}
int dfs(int u, int flow) {
if (u == T)
return flow;
int sum = 0;
for (int &i = cur[u]; i; i = e[i].next) {
int v = e[i].point;
if (!e[i].len || dep[v] != dep[u] + 1) continue;
int out = dfs(v, min(flow, e[i].len));
flow -= out, sum += out;
e[i].len -= out, e[i ^ 1].len += out;
if (!flow) break;
}
return sum;
}
void init(int x, int y) {
S = x, T = y;
cnt = 1;
for (int i = 1; i <= T; i ++)
first[i] = 0;
}
int dinic() {
int maxflow = 0;
while (bfs())
maxflow += dfs(S, 0x3f3f3f3f);
// cerr << "?maxflow:" << maxflow << endl;
return maxflow;
}
} net;
struct dat {
int v, id;
dat (int x = 0, int y = 0) {
v = x, id = y;
}
};
vector<dat> e[MAXN];
int cur[MAXN];
int ind[MAXN], outd[MAXN];
bool vis[MAXN];
stack<int> ans;
int cnt, tt;
pair<int, int> c[MAXN];
void dfs(int u, int id) {
for (int i = cur[u]; i >= 0; i = cur[u]) {
cur[u] --;
dfs(e[u][i].v, e[u][i].id);
}
if (id > 0) ans.push(id), cnt ++;
if (id == -2) cnt --;
if (id < -2) {
/*先楼下型消息,再楼上型消息*/
ans.push(c[- id].second);
ans.push(c[- id].first);
cnt += 2;
}
}
int getid(int x, int t) {
return x + t * n;
}
void del(int x, int v, int id) {
for (auto i = e[x].begin(); i != e[x].end(); i ++) {
if (i -> v == v && i -> id == id) {
e[x].erase(i);
break;
}
}
}
struct dat2 {
int u, v, p, id;
} b[MAXN];
void solve() {
// cerr << "?Text:" << endl;
int T = n * 2 + 1; cnt = 0;
for (int i = 1; i <= 2 * n + 1; i ++)
e[i].clear(),
ind[i] = outd[i] = cur[i] = 0;
int len = 0;
for (int i = 1; i <= m; i ++) {
vis[i] = 0;
if (a[i].p == 0) {
int u = id[a[i].s1], v = id[a[i].s2];
b[++ len] = (dat2) {v, u, 0, i};
}
if (a[i].p == 1) {
int u = id[a[i].s1], v = id[a[i].s2];
b[++ len] = (dat2) {u, v, 1, i};
}
}
sort(b + 1, b + len + 1, [](dat2 x, dat2 y){
if (x.u ^ y.u) return x.u < y.u;
if (x.v ^ y.v) return x.v < y.v;
return x.p > y.p;
});
tt = 3;
for (int i = 1, nt; i <= len; i = nt) {
for (nt = i + 1; b[nt].u == b[i].u && b[nt].v == b[i].v && nt <= len; nt ++);
int l = i, r = nt - 1;
// cerr << "?" << l << ' ' << r << ';' << b[i].u << ' ' << b[i].v << endl;
while (l < r && b[l].p != b[r].p) {
vis[b[l].id] = vis[b[r].id] = 1;
// cout << "?union:" << b[l].id << " " << b[r].id << endl;
int u = getid(b[l].u, 1), v = getid(b[l].v, 0);
c[++ tt] = make_pair(b[l].id, b[r].id);
e[u].emplace_back(v, - tt);
outd[u] ++, ind[v] ++;
l ++, r --;
}
}
for (int i = 1; i <= m; i ++) {
if (vis[i]) continue;
if (a[i].p == 2) { // 学术消息
int x = id[a[i].s1], u = getid(x, 0), v = getid(x, 1);
e[v].emplace_back(u, i);
outd[v] ++;
ind[u] ++;
cnt --;
} else {
int type = a[i].p;
int u = getid(id[a[i].s1], type), v = getid(id[a[i].s2], type);
if (type == 0) { // 楼下消息
e[v].emplace_back(u, i); // v -> u
outd[v] ++;
ind[u] ++;
} else { // 楼上消息
e[u].emplace_back(v, i); // u -> v
outd[u] ++;
ind[v] ++;
}
}
}
net.init(n * 2 + 1, n * 2 + 2);
for (int i = 1; i <= n; i ++) {
int u = getid(i, 0);
if (ind[u] < outd[u])
net.Add(u, net.T, outd[u] - ind[u], -1);
int v = getid(i, 1);
if (outd[v] < ind[v])
net.Add(net.S, v, ind[v] - outd[v], -1);
}
for (int i = 1; i <= m; i ++) {
if (vis[i]) continue;
if (a[i].p == 0) { // 楼下消息
int u = getid(id[a[i].s1], 1), v = getid(id[a[i].s2], 0);
net.Add(u, v, 1, i);
} else if (a[i].p == 1) { // 楼上消息
int u = getid(id[a[i].s2], 1), v = getid(id[a[i].s1], 0);
net.Add(u, v, 1, i);
}
}
net.dinic();
int tot = 0;
for (int i = 2; i <= net.cnt; i ++)
if (net.e[i].id > 0 && net.e[i ^ 1].len) {
// cerr << "?ban:" << net.e[i].id << endl;
int x = id[a[net.e[i].id].s1], y = id[a[net.e[i].id].s2], u = getid(x, 0), v = getid(x, 1);
if (a[net.e[i].id].s3 == "louxia") { // y -> x
x = getid(x, 0), y = getid(y, 0);
del(y, x, net.e[i].id);
outd[y] --;
ind[x] --;
} else { // x -> y
x = getid(x, 1), y = getid(y, 1);
del(x, y, net.e[i].id);
outd[x] --;
ind[y] --;
}
e[v].emplace_back(u, net.e[i].id);
ind[u] ++,
outd[v] ++;
tot ++;
cnt --;
}
// cerr << "!edgecnt:" << tot << endl;
for (int i = 1; i <= n; i ++) {
int u = getid(i, 0);
if (ind[u] > outd[u])
for (int j = 1; j <= ind[u] - outd[u]; j ++)
e[u].emplace_back(T, -1);
if (ind[u] < outd[u])
for (int j = 1; j <= outd[u] - ind[u]; j ++)
e[T].emplace_back(u, -2);
int v = getid(i, 1);
if (outd[v] > ind[v])
for (int j = 1; j <= outd[v] - ind[v]; j ++)
e[T].emplace_back(v, -1);
if (outd[v] < ind[v])
for (int j = 1; j <= ind[v] - outd[v]; j ++)
e[v].emplace_back(T, -2);
}
for (int i = 1; i <= 2 * n + 1; i ++)
cur[i] = int(e[i].size()) - 1;
dfs(T, -1);
cout << cnt << '\n';
// cerr << "?" << ans.size() << ' ' << m << endl;
assert(ans.size() == m);
// cout << ans.size() <<endl;
while (!ans.empty())
cout << ans.top() << ' ',
ans.pop();
cout << '\n';
}
}
void solve() {
id.clear();
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
string name;
cin >> name;
id[name] = i;
}
for (int i = 1; i <= m; i ++) {
cin >> a[i].s1 >> a[i].s2 >> a[i].s3;
a[i].id = i;
}
for (int i = 1; i <= m; i ++) {
if ((a[i].s3 != "loushang" && a[i].s3 != "louxia") || id.find(a[i].s2) == id.end()) // 学术消息
a[i].p = 2;
else if (a[i].s3 == "louxia") // 楼下消息
a[i].p = 0;
else // 楼上消息
a[i].p = 1;
}
solution::solve();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t;
cin >> t;
while (t --) {
solve();
}
return 0;
}