[噼昂!]problem(Pending)
壹、关于题目 ¶
还是没有时间去编......
贰、关于题解 ¶
哦?这道题 \(n\) 居然只有 \(10^5\),那不是 \(\mathcal O(n^2m)=\mathcal O(n^4)\) 随便跑啊。
这固然是一种美妙的解法,不过仍然有优化的空间。
◆ 结论 の 开端
其实这道题为经典的 \(\rm Gale-Ryser\) 型刻划定理模型:
设 \(P_m = p_1,p_2,\cdots,p_m\) 及 \(Q_n=q_1,q_2,\cdots,q_n\) 是两个由非负整数构成的不增序列。如果存在一个简单 \(X,Y\) 二部图使得 \(X\) 中的顶点的度分别为 \(p_1,p_2,\cdots,p_m\) 且 \(Y\) 中的顶点的度分别为 \(q_1,q_2,\cdots,q_n\),那么称序列对 \((P_m,Q_n)\) 是二部可图的。
而 \((P_m,Q_n)\) 是二部可图的充要条件为
\[\forall k\in [1,m],\sum_{i=1}^k p_i\le \sum_{i=1}^n\min(q_i,k) \;\text{holds} \]
知道这个,我们就可以做许多事情。
◆ 结论 の 使用
将结论转到这道题上面,即先将 \(a\) 从大到小排序,然后
赶脚右边的 \(\min (b_i,k)\) 很难搞定,我们可以做一个转换:记 \(c_k=\sum [b_i\ge k]\),那么右边
感觉上来说挺直观的,不解释了。
那么,不等式的成立就是
于是乎,使用线段树维护所有 \(k\) 对应的值得最小值就可以解决这个问题。
比较细节的一个地方是修改 \(a_i\) 的时候,由于我们不想改变其不降的规则,我们可以选择每次在相同值平台的左右端点改,这样就不会破坏 \(a\) 不降了。
时间复杂度 \(\mathcal O(n\log n)\).
◆ 结论 の 终焉
找了很多资料,没什么比较好的证明,维基百科使用矩阵,然鉴于我是线代战争的炮灰,便不了了之了。后来发现就从网络流方向入手,反而可能是更容易理解的。
先将 \(a_i\) 由大到小排序,不妨建立这样一个网络:从源点向每个左部点连边,其容量为 \(a_i\),从每个右部点向汇点连边,容量为 \(b_i\),每个左右部点之间都有一条容量为 \(1\) 的边。下设 \(S,T\) 分别表示左右部点的集合,\(x,y\) 分别为源汇点。
假设某种割边方案,\(x\to a_p(p\in [1,k])\) 的边都未被割掉,而 \(x\to a_q(q\in [k+1,n])\) 的边都被割掉,我们再设 \([S],[T]\) 表示最终割完,与 \(x\) 同一集合的左部点点集为 \([S]\),右部点点集为 \([T]\),那么该最小割方案的花费可以表示为
根据我们的前置条件,我们可以化简式子:
后面两项可以看成是,将某些 \(b_i\) 换成了 \(k\). 由于我们想要最小割,所以我们肯定将那些大于 \(k\) 的换掉,所以,存在不等式
由于 \([T]\) 是我们自己设定的,所以我们可以构造 \([T]\) 使得 \(b_i\ge k\) 的点都不在 \([T]\) 中,便达到了下界,同时,最小割有
显然,原图是二部可图,当且仅当
这就是说,对于任意 \(k\in[1,n]\),都有这个不等式成立,即
叁、关于标程 ¶
#include <bits/stdc++.h>
using namespace std;
// # define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
namespace Elaina {
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
#ifndef USING_STDIN
inline char freaGET() {
# define BUFFERSIZE 1 << 17
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
template <class T> inline T readret(T x) {
x=0; int f = 0; char c;
while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template <class T> inline void readin(T& x) { x = readret(T(1)); }
template <class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template <class T> inline void writc(T x, char s='\n') {
static int fwri_sta[55], fwri_ed = 0;
if(x < 0) putchar('-'), x = -x;
do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
putchar(s);
}
} using namespace Elaina;
const int maxn = 250000;
int n, m, q;
int a[maxn + 5], b[maxn + 5];
inline void input() {
readin(n, m);
rep(i, 1, n) readin(a[i]);
rep(i, 1, m) readin(b[i]);
}
int cnt[maxn + 5], tmp[maxn + 5], revTmp[maxn + 5];
ll preCnt[maxn + 5], preTmp[maxn + 5];
namespace saya {
ll mn[maxn << 2 ^ 3], tag[maxn << 2 ^ 3];
#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r
inline void pushup(int i) { mn[i] = min(mn[ls], mn[rs]); }
inline void add(int i, ll v) { mn[i] += v, tag[i] += v; }
inline void pushdown(int i) {
if(!tag[i]) return;
add(ls, tag[i]), add(rs, tag[i]), tag[i] = 0;
}
void build(int i = 1, int l = 1, int r = n) {
tag[i] = 0;
if(l == r) return mn[i] = preCnt[l] - preTmp[l], void();
build(_lhs), build(_rhs), pushup(i);
}
void modify(int ql, int qr, ll v, int i = 1, int l = 1, int r = n) {
if(ql > qr || qr < l || r < ql) return;
if(ql <= l && r <= qr) return add(i, v);
pushdown(i);
if(ql <= mid) modify(ql, qr, v, _lhs);
if(mid < qr) modify(ql, qr, v, _rhs);
pushup(i);
}
#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs
} // using namespace saya;
inline void prelude() {
rep(i, 1, m) ++cnt[b[i]];
// lawks! upper limit should be @p maxn
for(int i = maxn; i; --i) cnt[i] += cnt[i + 1];
memcpy(tmp + 1, a + 1, n << 2);
sort(tmp + 1, tmp + n + 1, [](const int x, const int y) { return x > y; });
memcpy(revTmp + 1, tmp + 1, n << 2);
reverse(revTmp + 1, revTmp + n + 1);
for(int i = 1; i <= n; ++i) {
preCnt[i] = preCnt[i - 1] + cnt[i];
preTmp[i] = preTmp[i - 1] + tmp[i];
}
saya::build();
}
inline void work() {
int q, op, id, pos;
readin(q);
while(q--) {
readin(op, id);
if(op == 2) {
pos = lower_bound(revTmp + 1, revTmp + n + 1, a[id]) - revTmp;
--revTmp[pos], --a[id];
saya::modify(n - pos + 1, n, 1);
}
else if(op == 1) {
pos = upper_bound(revTmp + 1, revTmp + n + 1, a[id]) - revTmp - 1;
++revTmp[pos], ++a[id];
saya::modify(n - pos + 1, n, -1);
}
else if(op == 4) saya::modify(b[id], n, -1), --b[id];
else ++b[id], saya::modify(b[id], n, 1);
writc((int)(saya::mn[1] >= 0));
}
}
signed main() {
// freopen("problem.in", "r", stdin);
// freopen("problem.out", "w", stdout);
input();
prelude();
work();
return 0;
}
肆、关键 の 地方 ¶
就是 \(\rm Gale-Ryser\) 定理咯~~~~