题解:P9104 [PA 2020] Królewski bal
转化题意,对两种表演方式(设为黑/白)建二分图,左侧黑右侧白,两个点连边当且仅当在同一行或者同一列。那么答案就是这张二分图的最大匹配。
最大匹配显然是不好做的,考虑转化为
最大独立集在原方阵中形如怎么样的呢?形如每一行/每一列中选的格子只会有一种颜色。也就是说可以通过如下方式得到一个独立集:枚举列、行中选择白色的集合
枚举列选择的方案,设集合
考虑每一行变为黑是否会增加答案。对于行上的点有四种情况:
- 目前为白,所在的列选白。
- 目前为白,所在的列选黑。
- 目前为黑,所在的列选白。
- 目前为黑,所在的列选黑。
若一行由白变为黑,那么对答案的贡献是情况
于是计算出
其中
对于第一次询问,可以扫描线预处理出
复杂度
代码中
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
int n, m, Q;
vector<int> qry[N];
vector<pair<int, int> > cg[N], cgg[N];
int qx[N], qy[N], val[N], to[N];
struct segtree{
int t[N*4], tag[N*4];
void psd(int p, int l, int r){
int mid = l + r >> 1;
if(tag[p]){
t[p<<1] = (mid - l + 1) - t[p<<1];
t[p<<1|1] = (r - mid) - t[p<<1|1];
tag[p<<1] ^= 1;
tag[p<<1|1] ^= 1;
tag[p] = 0;
}
}
void build(){
memset(t, 0, sizeof(t));
memset(tag, 0, sizeof(tag));
}
void rev(int p, int l, int r, int ql, int qr){
if(qr < l || r < ql){
return;
} else if(ql <= l && r <= qr){
t[p] = (r - l + 1) - t[p];
tag[p] ^= 1;
} else {
int mid = l + r >> 1;
psd(p, l, r);
rev(p<<1, l, mid, ql, qr);
rev(p<<1|1, mid+1, r, ql, qr);
t[p] = t[p<<1] + t[p<<1|1];
}
}
int ask(int p, int l, int r, int ql, int qr){
if(qr < l || r < ql){
return 0;
} else if(ql <= l && r <= qr){
return t[p];
} else {
int mid = l + r >> 1;
psd(p, l, r);
return ask(p<<1, l, mid, ql, qr) + ask(p<<1|1, mid+1, r, ql, qr);
}
}
} t;
int a[N], b[N];
int A[N];
int al[N], ar[N];
ll tb[N];
struct seggtree{
ll t[N*4], tag[N*4];
void psd(int p){
tag[p<<1] += tag[p];
tag[p<<1|1] += tag[p];
t[p<<1] += tag[p];
t[p<<1|1] += tag[p];
tag[p] = 0;
}
void add(int p, int l, int r, int ql, int qr, ll v){
if(qr < l || r < ql){
return;
} else if(ql <= l && r <= qr){
t[p] += v;
tag[p] += v;
} else {
int mid = l + r >> 1;
psd(p);
add(p<<1, l, mid, ql, qr, v);
add(p<<1|1, mid+1, r, ql, qr, v);
t[p] = max(t[p<<1], t[p<<1|1]);
}
}
ll qrymx(){
return t[1];
}
} tt;
map<int, int> id[N];
int idc;
int main(){
scanf("%d%d%d", &n, &m, &Q);
for(int i = 1; i <= m; ++ i){
int x, y, xx, yy;
scanf("%d%d%d%d", &x, &y, &xx, &yy);
cg[x].push_back(make_pair(y, yy));
cg[xx+1].push_back(make_pair(y, yy));
cgg[y].push_back(make_pair(x, xx));
cgg[yy+1].push_back(make_pair(x, xx));
}
for(int i = 1; i <= Q; ++ i){
int x, y;
scanf("%d%d", &x, &y);
qx[i] = x;
qy[i] = y;
qry[x].push_back(i);
}
t.build();
for(int i = 1; i <= n; ++ i){
for(pair<int, int> p : cg[i]){
t.rev(1, 1, n, p.first, p.second);
}
A[i] = a[i] = n - t.ask(1, 1, n, 1, n);
for(int p : qry[i]){
if(!id[i][qy[p]]){
id[i][qy[p]] = ++ idc;
}
val[id[i][qy[p]]] = t.ask(1, 1, n, qy[p], qy[p]);
}
}
t.build();
for(int i = 1; i <= n; ++ i){
for(pair<int, int> p : cgg[i]){
t.rev(1, 1, n, p.first, p.second);
}
b[i] = t.ask(1, 1, n, 1, n);
++ tb[1];
-- tb[b[i]+1];
}
sort(A + 1, A + n + 1);
reverse(A + 1, A + n + 1);
int la = n + 1;
for(int i = 1; i <= n + 1; ++ i){
ar[la] = i-1;
for(int j = la - 1; j >= A[i]; -- j){
al[j] = i;
ar[j] = i-1;
}
if(la != A[i] && A[i] >= 0){
la = A[i];
al[A[i]] = i;
}
}
for(int i = 1; i <= n; ++ i){
tb[i] += tb[i-1];
tt.add(1, 0, n, i, n, A[i]);
tt.add(1, 0, n, 0, i-1, tb[i]);
}
printf("%lld\n", 1ll * n * n - tt.qrymx());
for(int i = 1; i <= Q; ++ i){
if(val[id[qx[i]][qy[i]]] == 1){
int na = a[qx[i]];
++ a[qx[i]];
tt.add(1, 0, n, al[na], n, 1);
++ al[na];
++ ar[na+1];
-- b[qy[i]];
tt.add(1, 0, n, 0, b[qy[i]], -1);
} else {
int na = a[qx[i]];
-- a[qx[i]];
tt.add(1, 0, n, ar[na], n, -1);
-- ar[na];
-- al[na-1];
tt.add(1, 0, n, 0, b[qy[i]], 1);
++ b[qy[i]];
}
val[id[qx[i]][qy[i]]] ^= 1;
printf("%lld\n", 1ll * n * n - tt.qrymx());
}
return 0;
}