CF1386C. Joker(整体二分)

CF1386C. Joker

Solution

难得有一道可以整体二分的题。

有一个基本的思路是:考虑求出 A n s i Ans_i Ansi表示最小的右端点,满足 [ 1 , i ] ∪ [ A n s i , m ] [1,i]\cup[Ans_i,m] [1,i][Ansi,m]中存在奇环。

考虑到 A n s i Ans_i Ansi序列是非减的,因此我们可以 t w o    p o i n t e r s + L C T two\;pointers+LCT twopointers+LCT维护生成树以及是否存在奇环,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

不过还有一种 O ( n log ⁡ 2 n ) O(n \log^2n) O(nlog2n)的整体二分做法:
S o l v e ( l , r , L , R ) Solve(l,r,L,R) Solve(l,r,L,R)表示求出 i ∈ [ l , r ] , A n s i ∈ [ L , R ] i\in[l,r],Ans_i\in[L,R] i[l,r],Ansi[L,R]的所有 A n s i Ans_i Ansi。有一个精妙的想法是:我们去保证 S o l v e ( l , r , L , R ) Solve(l,r,L,R) Solve(l,r,L,R) [ 1 , l ] ∪ [ R , n ] [1,l]\cup[R,n] [1,l][R,n]的边已经加入 ,那么 i = m i d i=mid i=mid的答案就可以通过加入 [ l + 1 , m i d ] [l+1,mid] [l+1,mid]以及不超过 O ( R − L ) O(R-L) O(RL)条边求出,然后分治下去。

总时间复杂度: O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

Code

#include <bits/stdc++.h>
 
using namespace std;
 
template<typename T> inline bool upmin(T &x, T y) { return y < x ? x = y, 1 : 0; }
template<typename T> inline bool upmax(T &x, T y) { return x < y ? x = y, 1 : 0; }
 
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
 
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int, int> PR;
typedef vector<int> VI; 
 
const lod eps = 1e-9;
const lod pi = acos(-1);
const int oo = 1 << 30;
const ll loo = 1ll << 60;
const int mods = 1e9 + 7;
const int inv2 = (mods + 1) >> 1;
const int MAXN = 200005;
const int INF = 0x3f3f3f3f; //1061109567
/*--------------------------------------------------------------------*/
 
namespace FastIO{
	constexpr int SIZE = (1 << 21) + 1;
	int num = 0, f;
	char ibuf[SIZE], obuf[SIZE], que[65], *iS, *iT, *oS = obuf, *oT = obuf + SIZE - 1, c;
	#define gc() (iS == iT ? (iT = ((iS = ibuf) + fread(ibuf, 1, SIZE, stdin)), (iS == iT ? EOF : *iS ++)) : *iS ++)
	inline void flush() {
		fwrite(obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	inline void putc(char c) {
		*oS ++ = c;
		if (oS == oT) flush();
	}
	inline void getc(char &c) {
		for (c = gc(); c != '<' && c != '>' && c != EOF; c = gc());
	}
	inline void reads(char *st) {
		char c;
		int n = 0;
		getc(st[++ n]);
		for (c = gc(); c == '<' || c == '>' ; c = gc()) st[++ n] = c;
		st[n + 1] = '\0';
	}
	template<class I>
	inline void read(I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9' ; c = gc()) if (c == '-') f = -1;
		for (x = 0; c >= '0' && c <= '9' ; c = gc()) x = (x << 3) + (x << 1) + (c & 15);
		x *= f;
	}
	template<class I>
	inline void print(I x) {
		if (x < 0) putc('-'), x = -x;
		if (!x) putc('0');
		while (x) que[++ num] = x % 10 + 48, x /= 10;
		while (num) putc(que[num --]);
	}
	struct Flusher_{~Flusher_(){flush();}} io_Flusher_;
}
using FastIO :: read;
using FastIO :: putc;
using FastIO :: reads;
using FastIO :: print;
 
 
 
 
 
PR E[MAXN];
int f[MAXN], col[MAXN], Ans[MAXN], num[MAXN], top = 0, n, m, Case;
struct stknode{ int x, p, y, q; } stk[MAXN];

int find(int x) { return f[x] == x ? f[x] : find(f[x]); }
int getcol(int x) { return f[x] == x ? col[x] : getcol(f[x]) ^ col[x]; }
int Add(int u, int v) {
	int x = find(u), y = find(v);
	if (num[x] > num[y]) swap(x, y);
	if (x != y) {
		stk[++ top] = (stknode){x, col[x], y, num[y]};
		col[x] ^= getcol(u) ^ getcol(v) ^ 1;
		f[x] = y, num[y] += num[x];
		return 0;
	}
	return (getcol(u) == getcol(v));
}
void del(int x) {
	while (top > x) {
		f[stk[top].x] = stk[top].x;
		col[stk[top].x] = stk[top].p;
		num[stk[top].y] = stk[top].q;
		-- top;
	}
}
void solve(int l, int r, int L, int R, int flag) {
//	cout << l << " " << r << " " << L << " " << R << " " << flag << endl;
	if (l > r) return;
	if (flag || L == R) {
		for (int i = l; i <= r ; ++ i) Ans[i] = R;
		return;
	}

	int mid = (l + r) >> 1, lst = top, Flag = 0;
	for (int i = l + 1; i <= mid ; ++ i) Flag |= Add(E[i].fi, E[i].se);
	if (Flag) Ans[mid] = R;
	else {
		int Lst = top;
		for (int i = R - 1; i >= L ; -- i)
			if (Add(E[i].fi, E[i].se)) { Ans[mid] = i; break; }
		del(Lst);
	}
	Flag |= Add(E[mid + 1].fi, E[mid + 1].se);
	solve(mid + 1, r, Ans[mid], R, Flag);
	del(lst);

	Flag = 0;
	for (int i = R - 1; i >= Ans[mid] ; -- i) Flag |= Add(E[i].fi, E[i].se);
	solve(l, mid - 1, L, Ans[mid], Flag);
}
signed main() {
#ifndef ONLINE_JUDGE
	freopen("a.in", "r", stdin);
#endif
	read(n), read(m), read(Case);
	for (int i = 1; i <= n ; ++ i) f[i] = i, num[i] = 1;
	for (int i = 1; i <= m ; ++ i) read(E[i].fi), read(E[i].se);
	solve(0, m - 1, 1, m + 1, 0);
//	for (int i = 1; i <= n ; ++ i) cout << Ans[i] << endl;
	while (Case --) {
		int l, r;
		read(l), read(r);
		if (Ans[l - 1] > r) putc('Y'), putc('E'), putc('S');
		else putc('N'), putc('O');
		putc('\n');
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值