题解:CF1007D Ants

题目传送门

每只蚂蚁只走一对点肯定是不劣的,由此想到 2-sat。

限制条件是:若 (a,b)(c,d) 两条链相交,则不能同时选。直接建图肯定是爆炸的。

用树剖可以将 (a,b) 这条链划分成 O(logn) 个区间。因为同一条链的区间不交,限制条件变为若两个区间相交,则这两个点不能同时选。

把这些区间放到线段树上,限制条件变成:选了一个点,这个点在线段树上的祖先(包括自己)上的其他点都不能选。

先连左右儿子,再类似 这题 的前缀优化建图,可以满足线段树上一个区间上的点最多选一个。

具体来说记 fx,1k 表示 x 点上的前缀或,x 的左右儿子为 ls,rsx 上挂了 a1,a2ak

  1. fls,k,frs,kfx,0 连边。
  2. 连边 fx,i1fx,ifx,i1¬aiaifx,i,当然还要对称连边

最后跑 2-sat 就行了。

时间和空间复杂度都是 O(mlog2n)

Code:

#include<iostream> #include<cstdlib> #include<ctime> #include<cassert> #include<vector> #include<cstring> #include<algorithm> using namespace std; const int N = 5e6 + 10; int tot, n, m, top[N], son[N], sz[N], fa[N], dfn[N], dep[N]; int dfn1[N], low[N], c[N], st[N]; vector <int> e[N], pos[N]; void dfs1(int x, int pfa){ fa[x] = pfa, dep[x] = dep[pfa] + 1, sz[x] = 1; for(int y : e[x]){ if(y == pfa) continue; dfs1(y, x); sz[x] += sz[y]; if(sz[son[x]] < sz[y]) son[x] = y; } } void dfs2(int x, int pfa){ dfn[x] = ++dfn[0], top[x] = pfa; if(son[x]) dfs2(son[x], pfa); for(int y : e[x]) if(y != fa[x] && y != son[x]) dfs2(y, y); } void update(int id, int l, int r, int x, int y, int a){ if(x <= l && y >= r){ pos[id].push_back(a); return; } int mid = (l + r) >> 1; if(x <= mid) update(2 * id, l, mid, x, y, a); if(y > mid) update(2 * id + 1, mid + 1, r, x, y, a); } void tarjan(int x){ low[x] = dfn1[x] = ++dfn1[0], st[++st[0]] = x; for(int y : e[x]){ if(!dfn1[y]){ tarjan(y); low[x] = min(low[x], low[y]); } else if(!c[y]) low[x] = min(low[x], dfn1[y]); } if(dfn1[x] == low[x]){ c[x] = ++c[0]; while(st[st[0]] != x) c[st[st[0]--]] = c[0]; st[0]--; } } void update(int x, int y, int t){ while(top[x] != top[y]){ if(dep[top[x]] < dep[top[y]]) swap(x, y); update(1, 1, n, dfn[top[x]], dfn[x], t); x = fa[top[x]]; } if(dfn[x] > dfn[y]) swap(x, y); if(dfn[x] < dfn[y]) update(1, 1, n, dfn[x] + 1, dfn[y], t); } int solve(int id, int l, int r){ int la = 0; if(l != r){ int mid = (l + r) >> 1, x = solve(2 * id, l, mid), y = solve(2 * id + 1, mid + 1, r); if(!x || !y) la = x + y; else{ e[x].push_back(tot); e[tot ^ 1].push_back(x ^ 1); e[y].push_back(tot); e[tot ^ 1].push_back(y ^ 1); la = tot, tot += 2; } } for(int i : pos[id]){ if(la){ e[la].push_back(i ^ 1); e[i].push_back(la ^ 1); e[la].push_back(tot); e[tot ^ 1].push_back(la ^ 1); e[i].push_back(tot); e[tot ^ 1].push_back(i ^ 1); la = tot, tot += 2; } else la = i; } return la; } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n; for(int i = 1, u, v; i < n; i++){ cin >> u >> v; e[u].push_back(v); e[v].push_back(u); } dfs1(1, 0); dfs2(1, 1); for(int i = 1; i <= n; i++) e[i].clear(); cin >> m; for(int i = 1, a, b, c, d; i <= m; i++){ cin >> a >> b >> c >> d; update(a, b, 2 * i); update(c, d, 2 * i + 1); } tot = 2 * m + 2; solve(1, 1, n); for(int i = 1; i < tot; i++) if(!dfn1[i]) tarjan(i); for(int i = 1; i <= m; i++){ if(c[2 * i] == c[2 * i + 1]){ cout << "NO\n"; return 0; } } cout << "YES\n"; for(int i = 1; i <= m; i++) cout << 1 + (c[2 * i] > c[2 * i + 1]) << "\n"; }

__EOF__

本文作者louisliang
本文链接https://www.cnblogs.com/louisliang/p/18455768.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   louisliang  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示