20
http://pjudge.ac/contest/1288/problem/21777
考虑 A 性质怎么做,把每种颜色从 \(x\) 轴上面还是下面连过去视作 \(0/1\) 两种状态,然后优化建图跑二分图染色即可。
如果在一般情况沿用上面的做法,对合法性的刻画会变得十分复杂,因为两个点之间的连边可能会从整个数轴的左边或右边绕过去。但是我们发现,如果可以从左边绕过去,那也一定可以改成从右边绕过去,而不改变整个连边方式的合法性,如下图。

这启发我们,上面做法里的刻画方式是很不对的,因为经过这样一次改变,\(0/1\) 状态就变了。
考虑在这样一次改变里,什么东西没有变。从一个点延申出的线段方向是没有变的。所以设 \(x_i\) 表示点 \(i\) 延申出的线段方向,经过一些手玩,所有对合法性的刻画都可以写成如下形式:
- 若 \(l_i<l_j<r_j<r_i\),则 \(x_{l_j}=x_{r_j}\);
- 若 \(l_i<l_j<r_i<r_j\),则 \(x_{l_j}\not=x_{r_i}\)。
然后就做完了。
https://newoj.daimayuan.top/p/4775?tid=69a5374fbff5d4356a21b04f
问题可以变成这样:有 \(n\) 个长度为 \(n\) 的 bitset,你需要对于每一位,求出有多少个 bitset 上这一位为 \(1\)。
对 \(n\) 个 bitset 做分治,拆位模拟加法。
vector<int> solve(int l, int r) {
if (l == r) return {s[l]};
int mid = l + r >> 1, tmp = 0;
vector<int> sl = solve(l, mid), sr = solve(mid + 1, r), ans;
int mx = max(sl.size(), sr.size());
sl.resize(mx), sr.resize(mx), ans.resize(mx);
for (int i = 0; i < sl.size(); i++) {
ans[i] = sl[i] ^ sr[i] ^ tmp;
tmp = (sl[i] & sr[i]) | ((sl[i] | sr[i]) & tmp);
}
if (tmp) ans.push_back(tmp);
return ans;
}
设 \(f_{S,i}\) 表示集合 \(S\) 内的点组成的连通图,\(1\sim i\) 全都不是割点的方案数。那么有
\[f_{S,0}=2^{cnt_S}-\sum_{\min(S)\in T}f_{T,0}2^{cnt_{S-T}}
\]
\[f_{S,i}=f_{S,i-1}-\sum_{i\in T,\min(S-\set i)\in T}f_{T,i}f_{(S-T)\cup\set i,i-1}\
\]
答案就是 \(f_{S,n}\)。
注意到边双是由若干个大小不为 \(2\) 的点双拼起来组成的。设 \(g_{S,i}\) 表示集合 \(S\) 内的点组成的边双连通图,已经加入了 \(i+1\sim n\) 的割点的方案数,那么有
\[g_{S,n}=[|S|\not=2]f_{S,n}
\]
\[g_{S,i-1}=g_{S,i}+\sum_{i\in T,\min(S-\set i)\in T}g_{T,i}g_{(S-T)\cup\set i,i-1}\
\]
答案就是 \(g_{S,0}\)。
这完全就是把上面那个题的过程倒过来做了一遍。

浙公网安备 33010602011771号