qoj18434 I Will Always Remember You
给你一个 \(n\) 个点 \(m\) 条边的 DAG,点带颜色,有 \(q\) 次操作和查询
- 修改某个点的颜色
- 查询点 \(x\) 在
DAG上可达的点的颜色种类数。
对于这种 DAG 可达性问题我们一般直接考虑 bitset。
那么有一个很显然的想法就是我们令 \(f(i,j)\) 表示点 \(i\) 能否到达 \(j\),\(g(i,j)\) 表示 \(i\) 能否到达颜色为 \(j\) 的点。
那么在拓扑序上转移 \(f\),同时在每个点用 \(c_i\) 转移 \(g\) 即可做到静态回答询问。
对操作序列分块,处理一个块内的询问时把块内即将要修改的点先扣掉,即不令其更新 \(g\) 状态。
然后扫一遍块内的操作,在回答时枚举每一个被扣掉的点利用 \(f\) 更新 \(g\)。
这样即可做到 \(\mathcal O(\frac{nq}\omega)\)。
但不幸的是空间存不下。
我们考虑交换两维,设 \(f(i,j)\) 表示 \(i\) 能否被 \(j\) 抵达,\(g(i,j)\) 表示颜色 \(i\) 能否被 \(j\) 抵达。
这里会发现只有询问中的 \(j\) 是有用的。那么我们将其重标号,即可将第二位长度压缩至 \(B\)。
但还有一个问题是我们对 \(j\) 一维压位,但实际上需要对 \(i\) 这一维求和。
直觉上我们可以直接将每个 \(g(*,j)\) 加起来,但这样会产生进位。
可以通过维护其二进制分解 \(\sum g(*,j)=\sum 2^kg(k,j)\) 做到 \(\mathcal O(\frac{nm\log n}\omega)\) 每次求和。
更好的办法是,对 \(a,b,c\in[0,1]\),我们有:( \(\oplus\) 指 \(\text{xor}\)。
通过维护位权,可以将 \((a,b,c)\) 三个数变为 \((s,c)\) 两个数,具体可以参考代码。
删去一个数仅需 \(\mathcal O(1)\) 次操作,因此可以在 \(\mathcal O(n)\) 的时间内计算。
最优解(?
#include <algorithm>
#include <iostream>
#include <string.h>
#include <bitset>
#include <queue>
const int N = 150007;
int n, m, a[N];
struct node { int y, x; } q[N];
std::basic_string<int> g[N], gv[N];
int deg[N], topo[N];
void toposort() {
std::queue<int> q;
for(int i = 1; i <= n; ++i) {
if(!deg[i]) q.push(i);
}
int ind = 0;
while(!q.empty()) {
int u = q.front(); q.pop();
topo[++ind] = u;
for(auto& v: gv[u]) {
if(!--deg[v]) {
q.push(v);
}
}
}
}
const int B = 1536;
typedef std::bitset<B> BS;
int ans[N], t1[N];
std::basic_string<int> T1;
BS Rc[N], Rv[N];
int res[B];
void convolution(BS* p) {
int weight = 0, k = n;
while(k > 0) {
int pt = 0, las = 0;
for(int i = 1; i + 2 <= k; i += 2) {
BS &a = p[i], &b = p[i+1], &c = p[i+2];
BS x = a ^ b;
p[++pt] = (a & b) | (x & c);
c ^= x; las = i + 2;
}
if(k % 2 == 0) {
BS x = p[k-1] ^ p[k];
p[++pt] = p[k-1] & p[k];
p[k] = x;
}
for(int i = 0; i < B; ++i)
res[i] |= int(p[k][i]) << weight;
++weight, k = pt;
}
}
inline void work(int l, int r) {
for(int i = 1; i <= n; ++i) {
Rc[i].reset(), Rv[i].reset();
}
int m2 = 0;
T1.clear();
for(int i = l; i <= r; ++i) {
int x = q[i].x;
if(q[i].y) {
if(!t1[x]++) T1 += x;
} else Rv[x][m2++] = 1;
}
for(int j = n; j >= 1; --j) {
int i = topo[j];
if(!t1[i]) Rc[a[i]] |= Rv[i];
for(int& v: g[i]) Rv[v] |= Rv[i];
}
m2 = 0;
for(int i = l; i <= r; ++i) {
if(q[i].y) a[q[i].x] = q[i].y;
else {
int x = q[i].x, ind = m2++;
for(auto& y: T1) {
if(Rv[y][ind] && !Rc[a[y]][ind]) {
Rc[a[y]][ind] = 1;
}
}
}
}
for(auto& x: T1) t1[x] = 0;
for(int i = 0; i < m2; ++i) res[i] = 0;
convolution(Rc);
m2 = 0;
for(int i = l; i <= r; ++i)
if(!q[i].y) ans[i] = res[m2++];
}
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
std::cin >> n >> m;
for(int i = 1; i <= n; ++i) std::cin >> a[i];
for(int i = 1; i <= m; ++i) {
int x, y; std::cin >> x >> y;
g[x] += y, ++deg[x], gv[y] += x;
}
toposort();
std::cin >> m;
for(int i = 1; i <= m; ++i) {
int op; std::cin >> op;
if(op == 1) std::cin >> q[i].x >> q[i].y;
else std::cin >> q[i].x;
}
int q1 = 0, q2 = 0, las = 0;
for(int i = 1; i <= m; ++i) {
++(q[i].y ? q1: q2);
if(i == m || q2 >= B || q1 >= B) {
work(las + 1, i);
las = i, q1 = q2 = 0;
}
}
for(int i = 1; i <= m; ++i)
if(!q[i].y) std::cout << ans[i] << "\n";
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/22558757

浙公网安备 33010602011771号