P9170 [省选联考 2023] 填数游戏 题解
先考虑怎么判断
对于
考虑每天边 Bob 会怎样?显然是选择其中一个端点,或者可以说,对无向边定向,从没选的指向选了的。
对于每个连通块,如果存在边数大于点数,那么无论怎么选都有重复,即为
这样我们已经判断了是否可以凑出每个都不同。那么接着就已经离正解不远了。
注意到当边数大于点数,输出
我们现在将
先考虑基环树。比较显然我们的边定向方案是一棵外向基环树,不在环上的边只能往外定向,这些贡献可以直接处理。环上只有两种,都选左端点或者右端点。环上的边的
考虑另一部分,即树。对于 Bob 来说,选法有树的点数种,每种都是确定一个根,然后连。直接做是
我们称
总体复杂度线性对数,
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
using namespace std;
constexpr int N = 2e6 + 5;
int T, n, m;
int e[N * 2], h[N * 2], ne[N * 2], gg[N * 2], idx;
int s[N], ss[N][3], t[N], tt[N][3];
void add(int u, int v, int id)
{
e[idx] = v, gg[idx] = id, ne[idx] = h[u], h[u] = idx++;
}
int cicp;
bool vis[N];
int cv, ce;
int fa[N];
bool cir;
bool both[N];
int dep[N];
vector<int> vv;
void dfs(int u, int from)
{
vv.emplace_back(u);
vis[u] = 1;
cv++;
int gg = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (i == (from ^ 1)) continue;
if (!vis[j] || dep[j] <= dep[u]) ce++;
if (j == u)
{
gg++;
}
if (vis[j])
{
cicp = j;
}
if (!vis[j])
{
dep[j] = dep[u] + 1;
fa[j] = u;
dfs(j, i);
}
}
if (gg) ce -= gg / 2;
}
class SegmentTree
{
public:
struct Node
{
int l, r, minn, tag;
}tr[N << 2];
void pushup(int u)
{
tr[u].minn = min(tr[u << 1].minn, tr[u << 1 | 1].minn);
}
void build(int u, int l, int r)
{
tr[u] = { l, r, 0, 0 };
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void pushdown(int u)
{
if (tr[u].tag)
{
tr[u << 1].minn += tr[u].tag;
tr[u << 1].tag += tr[u].tag;
tr[u << 1 | 1].minn += tr[u].tag;
tr[u << 1 | 1].tag += tr[u].tag;
tr[u].tag = 0;
}
}
void update(int u, int l, int r, int v)
{
//if (l > m) exit(-1);
if (tr[u].l >= l and tr[u].r <= r)
{
tr[u].tag += v;
tr[u].minn += v;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) update(u << 1, l, r, v);
if (r > mid) update(u << 1 | 1, l, r, v);
pushup(u);
}
int query(int u, int l, int r)
{
if (tr[u].l >= l and tr[u].r <= r) return tr[u].minn;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1, res = (int)1e9;
if (l <= mid) res = query(u << 1, l, r);
if (r > mid) res = min(res, query(u << 1 | 1, l, r));
return res;
}
}sgt;
bool cmp(int x)
{
return (ss[x][1] == tt[x][1] && ss[x][2] == tt[x][2]) || (ss[x][1] == tt[x][2] && ss[x][2] == tt[x][1]);
}
int tr_idx[N], tridx, trsz[N];
int res = 0, ans;
bool inc[N];
bool flag[N];
void pre_dfs(int u, int fa)
{
tr_idx[u] = ++tridx;
trsz[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
pre_dfs(j, u);
trsz[u] += trsz[j];
int id = gg[i];
if (s[id] == 2 && t[id] == 2 && cmp(id))
{
both[i] = 1;
sgt.update(1, 1, m, 1);
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, -1);
}
else if (s[id] == 2)
{
if (ss[id][1] == j || ss[id][2] == j)
{
sgt.update(1, 1, m, 1);
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, -1);
}
else if (ss[id][1] == u || ss[id][2] == u)
{
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, 1);
}
}
else
{
if (ss[id][1] == j)
{
sgt.update(1, 1, m, 1);
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, -1);
}
else if (ss[id][1] == u)
{
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, 1);
}
}
}
}
int nowl, nowr;
void dfs2(int u, int fa)
{
res = max(res, sgt.query(1, nowl, nowr));
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
if (both[i])
{
//cout << "!!!: " << u << " " << j << "\n";
sgt.update(1, 1, m, -1);
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, 2);
}
dfs2(j, u);
if (both[i])
{
sgt.update(1, 1, m, 1);
sgt.update(1, tr_idx[j], tr_idx[j] + trsz[j] - 1, -2);
}
}
}
void solve_tree(int u)
{
nowl = tridx + 1;
nowr = tridx + cv;
for (int i = nowl; i <= nowr; i++) sgt.update(1, i, i, -sgt.query(1, i, i));
pre_dfs(u, -1);
res = sgt.query(1, nowl, nowr);
//cout << "!!!: " << nowl << " " << nowr << " " << res << "\n";
//if (u == 2) cout << "yoxi: " << res << " " << nowl << " " << nowr << "\n";
dfs2(u, -1);
ans += res;
}
int tc;
int ccnt, c1, c2;
int get_res(int id)
{
if (ss[id][1] == tt[id][1]) return tt[id][1];
else if (t[id] >= 2 && ss[id][1] == tt[id][2]) return tt[id][2];
if (s[id] == 1) return -1;
else
{
if (ss[id][2] == tt[id][1]) return tt[id][1];
else if (t[id] >= 2 && ss[id][2] == tt[id][2]) return tt[id][2];
return -1;
}
}
namespace looped_base_tree
{
void dfs(int u, int f, int from)
{
vis[u] = 1;
fa[u] = f;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (i == (from ^ 1)) continue;
if (vis[j])
{
if (dep[u] < dep[j]) continue;
int now = u;
if (u == j)
{
inc[u] = 1;
continue;
}
do
{
inc[now] = 1;
now = fa[now];
} while (now != j);
inc[j] = 1;
}
else
{
dep[j] = dep[u] + 1;
dfs(j, u, i);
}
}
}
void dfs2(int u)
{
vis[u] = 1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
int id = gg[i];
if (!flag[i] && !flag[i ^ 1])
{
flag[i] = flag[i ^ 1] = 1;
if (inc[u] && inc[j])
{
if (s[id] == 2 && t[id] == 2 && cmp(id))
{
ccnt++;
}
else
{
int g = get_res(id);
if (~g)
{
if (g == u) c1++;
if (g == j) c2++;
}
}
}
else
{
bool fg = 0;
for (int k = 1; k <= s[id]; k++)
{
if (ss[id][k] == j)
{
fg = 1;
break;
}
}
if (fg) tc++;
}
}
if (!vis[j]) dfs2(j);
}
}
}
void solve_looped_base_tree(int u)
{
tc = c1 = c2 = ccnt = 0;
for (auto& i : vv) vis[i] = dep[i] = 0;
looped_base_tree::dfs(cicp, 0, -1);
for (auto& i : vv) vis[i] = 0;
looped_base_tree::dfs2(cicp);
int res = 0;
for (int i = 0; i <= ccnt; i++) res = max(res, min(c1 + i, c2 + ccnt - i));
ans += res + tc;
}
int main()
{
//freopen("C:\\Users\\60215\\Downloads\\Compressed\\game\\game\\game9.in", "r", stdin);
//freopen("C:\\Users\\60215\\Downloads\\Compressed\\game\\game\\game9.out", "w", stdout);\
memset(h, -1, sizeof h);
ios::sync_with_stdio(0), cin.tie(0);
cin >> T;
while (T--)
{
ans = 0;
cin >> n >> m;
sgt.build(1, 1, m);
for (int i = 1; i <= m; i++) trsz[i] = dep[i] = inc[i] = 0, h[i] = -1, vis[i] = fa[i] = 0;
for (int i = 0; i <= idx; i++) both[i] = flag[i] = 0;
idx = tridx = 0;
res = 0;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
for (int j = 1; j <= s[i]; j++) cin >> ss[i][j];
}
for (int i = 1; i <= n; i++)
{
cin >> t[i];
for (int j = 1; j <= t[i]; j++) cin >> tt[i][j];
if (t[i] == 1) add(tt[i][1], tt[i][1], i), add(tt[i][1], tt[i][1], i);
else add(tt[i][1], tt[i][2], i), add(tt[i][2], tt[i][1], i);
}
for (int i = 1; i <= m; i++)
{
if (vis[i]) continue;
cv = ce = cir = 0;
vv.clear(), vv.shrink_to_fit();
dep[i] = 0;
cicp = -1;
dfs(i, -1);
if (ce > cv)
{
cout << "-1\n";
goto E;
}
if (ce == cv - 1)
{
solve_tree(i);
}
else
{
solve_looped_base_tree(i);
}
}
cout << ans << "\n";
E:;
}
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通