P7843 「C.E.L.U-03」布尔
作者:互联网
Statement
给你 \(n\) 个布尔变量和 \(m\) 个限制,设 \(s_i\) 为 \(i\) 的取值。第 \(i\) 个限制形如 \(s_{u_i}\) 为 \(x_i\) 则 \(s_{v_i}\) 必须为 \(y_i\),同时如果 \(s_{v_i}\) 为 \(y_i\) 则 \(s_{u_i}\) 必须取 \(x_i\)。
一共 \(q\) 次询问,每次询问给出一个区间 \(l,r\)。求最少把 \(l,r\) 划分成多少段连续的区间,使得每段里的限制都可以得到一组合法解。如果无论如何都无法得到合法解,输出 -1
。
\(\textbf{Data Range:} 1\le n\le10^5,1\le m\le6\times10^5,1\le q\le10^6,1\le u,v\le n,1\le l\le r\le m,x,y\in \{0,1\}\)
Solution
首先,我们将每个变量 \(i\) 进行拆点 \(i, i+ n\),拆成两个点表示值为 \(\{0, 1\}\)。
然后每次连边要求选 \(x\) 则必须选 \(y\),那么 \(x \rightarrow y\),由于这题要求反之也必须取值。
那么每次连边都是连的一个无向边,我们可以用一个并查集进行维护每个点,然后连边就是并查集的合并。
然后如果有一个点 \(i\) 和 \(i + n\) 在同一个集合里,那么就是冲突了,于是就没有合法解了。
询问是对区间询问最少划分多少段每段都有解。
那么我们可以求出 \(f_i\) 表示以 \(i\) 为左端点,往右最多延伸到哪里还有合法解。
暴力 \(f\) 求我们显然可以 \(m^2\),然后,对于询问,我们就可以考虑暴力的倍增跳 \(f\),我们预处理一个倍增数组往后跳,然后对询问直接暴力跳就是 \(q\log m\) 的。
那么瓶颈在于对于 \(f\) 的求解上,额,这个显然有单调性,\(f\) 肯定是单调不减的,因为少了限制之后,不可能反而会提前不合法。
那么根据这个进行处理,我们现在首先卡出了 \(i\) 的一个向右最多能延伸的区间。
然后现在要转向去求 \(i + 1\) 的,那首先需要删除 \(i\) 这里的限制,然后往右边延伸继续加入限制,但是这需要实时支持删除,而并查集只能撤销,额,那换成 \(lct\) 来维护联通快,直接的进行 \(link\) 合并两个联通块就可以支持这个操作了。
然后如何判断不合法呢,你加入限制之后,你显然是因为你这两个点带来了影响,于是你分别查询你这两个点对应的另外一个取值点是否和他自己在一个联通快内就好了。
然后有个大问题,你发现你可能加边的时候形成了一个环,这种情况怎么办呢?
我们反正最后加进去的都是要寄的,到了后面,依次寄,寄的时候是从加入的先后顺序从先往后寄,那么要寄就先寄先加进去的,于是我们保留加入时间最晚的边就好了,然后每次要成环的时候去找出这个环上加入时间最小的,删除掉。
这样相当于是给他带权了,那么也就是维护了一个最大生成树,额,这个你常规连边的两个点换成新建一个点向两个点进行连边,然后就好了。
那么这个尺取的复杂度为 \(m\log n\) 的。
总复杂度 \(m \log n + q \log m\)。
详细细节可以参考代码实现。
// 德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱德丽莎你好可爱
// 德丽莎的可爱在于德丽莎很可爱,德丽莎为什么很可爱呢,这是因为德丽莎很可爱!
// 没有力量的理想是戏言,没有理想的力量是空虚
#include <bits/stdc++.h>
#define LL long long
using namespace std;
char ibuf[1 << 15], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 15, stdin), p1==p2) ? EOF : *p1++)
inline int read() {
char ch = getchar(); int x = 0, f = 1;
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x * f;
}
void print(LL x) {
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
template<class T> bool chkmin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool chkmax(T &a, T b) { return a < b ? (a = b, true) : false; }
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define repd(i, l, r) for (int i = (l); i >= (r); i--)
#define REP(i, l, r) for (int i = (l); i < (r); i++)
const int N = 6e5 + 5, M = 2e6, inf = 1e7;
int n, m, q;
int u[N], x[N], v[N], y[N], f[N][22], id[N][2];
int w[M], qwq[N][2];
pair <int,int> pt[M];
class LCT {
public :
struct node { int fa, ch[2], tag, val, id; } t[M];
int st[M], tp;
#define ls(k) t[k].ch[0]
#define rs(k) t[k].ch[1]
void pushup(int x) {
t[x].id = x; t[x].val = w[x];
if (t[ls(x)].val < t[x].val && t[ls(x)].id != 0) {
t[x].val = t[ls(x)].val;
t[x].id = t[ls(x)].id;
}
if (t[rs(x)].val < t[x].val && t[rs(x)].id != 0) {
t[x].val = t[rs(x)].val;
t[x].id = t[rs(x)].id;
}
}
void pushdown(int x) {
if( t[x].tag ) { swap( ls(x), rs(x) ); t[x].tag ^= 1; t[ls(x)].tag ^= 1; t[rs(x)].tag ^= 1; }
}
int chk(int x) { return (ls(t[x].fa) != x) && (rs(t[x].fa) != x); }
void rotate(int x) {
int f = t[x].fa, ff = t[f].fa; int z = (rs(f) == x), ch = t[x].ch[z ^ 1];
t[x].fa = ff; if( !chk(f) ) t[ff].ch[rs(ff) == f] = x; t[f].fa = x; t[x].ch[z ^ 1] = f; t[ch].fa = f; t[f].ch[z] = ch;
pushup(f), pushup(x);
}
void splay(int x) {
int u = x; st[++tp] = x; while(!chk(u)) st[++tp] = (u = t[u].fa); while(tp) pushdown(st[tp--]);
while(!chk(x)) {
int f = t[x].fa, ff = t[f].fa;
if(!chk(f)) { ( ( rs(ff) == f ) ^ ( rs(f) == x ) ) ? rotate(x) : rotate(f); } rotate(x);
}
}
void access(int x) { for(int y = 0; x; y = x, x = t[x].fa) { splay(x); rs(x) = y; pushup(x); } }
void makert(int x) { access(x); splay(x); t[x].tag ^= 1; }
int findrt(int x) { access(x); splay(x); pushdown(x); while(ls(x)) pushdown(x = ls(x)); return x; }
void split(int x,int y) { makert(x); access(y); splay(y); }
void cut(int x,int y) {
if(findrt(x) != findrt(y)) return;
split(x, y);
if(t[x].fa != y || t[x].ch[1]) return;
t[x].fa = t[y].ch[0] = 0; pushup(x);
// cout << "Cut : "<< x << " " << y << "\n";
return;
}
void link(int x,int y) {
makert(x);
// cout << "Link : " << x << " " << y << "\n";
if (findrt(y) != x) {
t[x].fa = y;
} else {
split(x, y);
int now = t[y].id;
// cout << now << "qwqwqwq\n";
// cout << pt[now].first << " " << pt[now].second << "\n";
cut(now, pt[now].first);
cut(now, pt[now].second);
makert(x);
t[x].fa = y;
}
}
}A;
void solve() {
n = read(), m = read(), q = read();
rep (i, 1, m) {
u[i] = read(), x[i] = read(), v[i] = read(), y[i] = read();
}
rep (i, 1, n) id[i][0] = i, id[i][1] = i + n;
memset(w, 127, sizeof(w));
// rep (i, 1, 2 * n) w[i] = inf;
int r = 1;
rep (i, 1, m) {
qwq[i][0] = 2 * n + i;
qwq[i][1] = 2 * n + m + i;
w[ qwq[i][0] ] = i * 2 - 1;
pt[ qwq[i][0] ] = {id[u[i]][x[i]], id[v[i]][y[i]]};
w[ qwq[i][1] ] = i * 2;
pt[ qwq[i][1] ] = {id[u[i]][x[i] ^ 1], id[v[i]][y[i] ^ 1]};
}
rep (i, 1, m) {
while (r <= m) {
int now = qwq[r][0];
A.link(id[u[r]][x[r]], now);
A.link(now, id[v[r]][y[r]]);
now = qwq[r][1];
A.link(id[u[r]][x[r] ^ 1], now);
A.link(now, id[v[r]][y[r] ^ 1]);
if (A.findrt( id[ u[r] ][ x[r] ] ) == A.findrt( id[ u[r] ][ (x[r] ^ 1) ] )) {
now = qwq[r][0];
A.cut(now, id[u[r]][x[r]]);
A.cut(now, id[v[r]][y[r]]);
now = qwq[r][1];
A.cut(now, id[u[r]][x[r] ^ 1]);
A.cut(now, id[v[r]][y[r] ^ 1]);
break;
}
// cout << id[v[r]][y[r]] << "" << id[v[r]][y[r] ^ 1] << "\n";
// cout << A.findrt( id[ v[r] ][ y[r] ] )<< " " << A.findrt( id[ v[r] ][ (y[r] ^ 1) ] ) << "\n";
if (A.findrt( id[ v[r] ][ y[r] ] ) == A.findrt( id[ v[r] ][ (y[r] ^ 1) ] )) {
now = qwq[r][0];
A.cut(now, id[u[r]][x[r]]);
A.cut(now, id[v[r]][y[r]]);
now = qwq[r][1];
A.cut(now, id[u[r]][x[r] ^ 1]);
A.cut(now, id[v[r]][y[r] ^ 1]);
break;
}
// cout << "????????\n";
r++;
}
f[i][0] = r - 1;
int now = qwq[i][0];
A.cut(id[u[i]][x[i]], now);
A.cut(now, id[v[i]][y[i]]);
now = qwq[i][1];
A.cut(id[u[i]][x[i] ^ 1], now);
A.cut(now, id[v[i]][y[i] ^ 1]);
}
// rep (i, 1, m) cout << f[i][0] << " "; cout << "\n";
rep (i, 1, m) f[i][0]++;
f[m + 1][0] = m + 1;
// rep (i, 1, m + 1) cout << f[i][0] << " ";
// cout << "\n";
rep (j, 1, 20)
rep(i, 1, m + 1)
f[i][j] = f[ f[i][j - 1] ][j - 1];
rep (c, 1, q) {
int l = read(), r = read();
int ans = 0;
repd (i, 20, 0) {
if (f[l][i] <= r) {
ans += (1 << i);
if (l == f[l][i]) { ans = -1; break; }
l = f[l][i];
}
}
if (ans == -1) { puts("-1"); continue; }
print(ans + 1); putchar('\n');
}
}
signed main () {
#ifdef LOCAL_DEFINE
freopen("1.in", "r", stdin);
freopen("1.ans", "w", stdout);
#endif
int T = 1; while (T--) solve();
#ifdef LOCAL_DEFINE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}
另一种做法是 yjz 说的直接对着东西进行决策单调性的分治,也就是整体二分。
总结
对于成环的时候我们可以考虑哪条边要被删除,然后钦定排除会没用的,这样就是一个使用 lct 构建最小/大生成树的问题了。
决策单调性除了分治还可以想想从尺取上下手,也许会很方便!
没事多想想分治~
我是 anton 的小粉丝~~
标签:03,ch,val,rs,int,P7843,fa,ls,布尔 来源: https://www.cnblogs.com/ptno/p/16522083.html