题解:P9104 [PA 2020] Królewski bal

· · 题解

题意

用矩形若干异或的形式给出一个 n\times n01 矩阵,把 0 看做左部点,1 看做右部点,如果两个不同值的点在同一行或同一列则连一条边。

q 次修改,每次反转矩阵中一个位置的值,你需要实时维护这个二分图的最大匹配。

### 题解 最大匹配肯定是不能直接维护的,有两种思路: 1. 维护 $cnt_{0}-\max\{|S|-|N(S)|\}$。 2. 维护 $n^2-\max |S_{独立集}|$。 实际上联立一下发现两个式子可以化为 $\max |S_{独立集}|=cnt_1+\max\{|S|-|N(S)|\}$,这个式子本身可以用调整法说明,即上述两个思路的式子等价,因此我们无需纠结到底用哪个。 考虑维护最大独立集大小,假设我们选择的若干个 $0$,那么可以选择与这些 $0$ 不同行且不同列的的 $1$,并且必定全选。 那么贪心地,我们肯定选择若干行和列,并且将这些行和列的交点上的 $0$ 全选,不在这些行列交点的 $1$ 全选。 记我们选择的行编号集合为 $S$,列编号集合为 $T$,同时计算 $S$ 和 $T$ 是不好做的,考虑使用调整法求最大独立集。 固定 $T$ 为全集,删掉若干个 $t\in T$。 令第 $i$ 行的 $0$ 个数为 $a_i$,第 $i$ 列的 $1$ 个数为 $b_i$,扫描线求得 $a$ 和 $b$。 初始时,最大独立集为 $\sum\limits_{i\in S} a_i$。 删掉一个 $t$,会删除第 $t$ 列中行编号在 $S$ 内的 $0$,加上行编号不在 $S$ 内的 $1$。 独立集大小的变化量为列内的 $S$ 外的 $1$ 个数减去 $S$ 内的 $0$ 个数,即 $S$ 外 $1$ 的个数减去 $|S|$ 再加上 $S$ 中 $1$ 的个数,即 $b_i-|S|$。 我们发现调整时每列独立,且变化量只与 $|S|$ 有关。 贪心地,我们枚举 $|S|$,选择前 $|S|$ 大的 $a$,之后求 $\sum \max(0,b_i-|S|)$。 那么我们可以轻松得到 $O(nq)$ 的做法。 写一下最大独立集大小的式子,先把 $a$ 从大到小排序。 $$ \max |S_{独立集}|=\max\limits_{x=0}^{n} \{sa_x+\sum\limits_{i=1}^{n} \max(0,b_i-x)\} \\ \max |S_{独立集}|=\max\limits_{x=0}^{n} \{sa_x+\sum\limits_{i=1}^{n} [b_i\geq x]b_i-x\sum\limits_{i=1}^{n} [b_i\geq x]1\} $$ 我们发现这个东西可以用线段树维护,具体地令 $f(x)=sa_x+\sum\limits_{i=1}^{n} [b_i\geq x]b_i-x\sum\limits_{i=1}^{n} [b_i\geq x]1$,开一颗线段树维护 $f$,每次会把 $a$ 的某一位和 $b$ 的某一位加减 $1$,每次修改实现 $a$ 的单点加减,实时维护 $a$ 有序并维护 $a$ 的前缀和,$b$ 的修改也是好维护的。 ```cpp #include<bits/stdc++.h> using namespace std; #define ll long long ll n,m,q; vector<pair<ll,ll>> Row[300005],Col[300005]; ll a[300005],b[300005]; struct _sgt1{ struct node{ ll l,r,sum,tag; }t[1200005]; void pushup(ll p){ t[p].sum=t[p*2].sum+t[p*2+1].sum; } void rev(ll p){ t[p].tag^=1,t[p].sum=t[p].r-t[p].l+1-t[p].sum; } void pushdown(ll p){ if(t[p].tag){ rev(p*2),rev(p*2+1); t[p].tag=0; } } void build(ll p,ll l,ll r){ t[p].sum=t[p].tag=0; t[p].l=l,t[p].r=r; if(l==r) return; ll mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); } void modify(ll p,ll l,ll r){ if(l<=t[p].l&&t[p].r<=r){ rev(p); return; } pushdown(p); ll mid=(t[p].l+t[p].r)/2; if(mid>=l) modify(p*2,l,r); if(mid<r) modify(p*2+1,l,r); pushup(p); } ll query(ll p,ll pos){ if(t[p].l==t[p].r) return t[p].sum; ll mid=(t[p].l+t[p].r)/2; pushdown(p); if(mid>=pos) return query(p*2,pos); else return query(p*2+1,pos); } }sgt1; struct _sgt2{ struct node{ ll l,r,mx,tag; }t[1200015]; void pushup(ll p){ t[p].mx=max(t[p*2].mx,t[p*2+1].mx); } void add(ll p,ll v){ t[p].mx+=v,t[p].tag+=v; } void pushdown(ll p){ if(t[p].tag){ add(p*2,t[p].tag),add(p*2+1,t[p].tag); t[p].tag=0; } } void build(ll p,ll l,ll r,ll arr[]){ t[p].l=l,t[p].r=r; if(l==r){ t[p].mx=arr[l]; return; } ll mid=(l+r)/2; build(p*2,l,mid,arr); build(p*2+1,mid+1,r,arr); pushup(p); } void modify(ll p,ll l,ll r,ll v){ if(l<=t[p].l&&t[p].r<=r){ add(p,v); return; } pushdown(p); ll mid=(t[p].l+t[p].r)/2; if(mid>=l) modify(p*2,l,r,v); if(mid<r) modify(p*2+1,l,r,v); pushup(p); } }sgt2; struct qry{ ll x,y,col,id; }qr[300005]; ll cntb[300005],res0[300005]; set<ll> S[300005]; struct aval{ ll a,id; }A[300005]; ll pos[300005]; ll bound(ll x){ ll l=1,r=n,p=0; while(l<=r){ ll mid=(l+r)/2; if(A[mid].a>=x){ p=mid; l=mid+1; }else r=mid-1; } return p; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n>>m>>q; for(ll i=1;i<=m;i++){ ll x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2; Row[x1].push_back({y1,y2}); Row[x2+1].push_back({y1,y2}); Col[y1].push_back({x1,x2}); Col[y2+1].push_back({x1,x2}); } for(ll i=1;i<=q;i++){ cin>>qr[i].x>>qr[i].y; qr[i].id=i; } sort(qr+1,qr+1+q,[&](qry x,qry y){ return x.x<y.x; }); ll Pos=0; sgt1.build(1,1,n); for(ll x=1;x<=n;x++){ for(auto opt:Row[x]){ ll l=opt.first,r=opt.second; sgt1.modify(1,l,r); } while(Pos<q&&qr[Pos+1].x<=x){ Pos++; qr[Pos].col=sgt1.query(1,qr[Pos].y); } a[x]=n-sgt1.t[1].sum; } sort(qr+1,qr+1+q,[&](qry x,qry y){ return x.id<y.id; }); sgt1.build(1,1,n); for(ll y=1;y<=n;y++){ for(auto opt:Col[y]){ ll l=opt.first,r=opt.second; sgt1.modify(1,l,r); } b[y]=sgt1.t[1].sum; } for(ll i=1;i<=n;i++) A[i]={a[i],i}; sort(A+1,A+1+n,[&](aval x,aval y){ return x.a>y.a; }); for(ll i=1;i<=n;i++) res0[i]=res0[i-1]+A[i].a; for(ll i=1;i<=n;i++) pos[A[i].id]=i; ll sumb=0,cnt=0; for(ll i=1;i<=n;i++){ cntb[b[i]]++; cnt++,sumb+=b[i]; } for(ll x=0;x<=n;x++){ res0[x]+=sumb-x*cnt; sumb-=cntb[x]*x; cnt-=cntb[x]; } sgt2.build(1,0,n,res0); cout<<n*n-sgt2.t[1].mx<<"\n"; for(ll i=1;i<=q;i++){ ll x=qr[i].x,y=qr[i].y; if(S[x].count(y)) qr[i].col=!qr[i].col; if(S[x].count(y)) S[x].erase(y); else S[x].insert(y); if(qr[i].col){ ll p=bound(A[pos[x]].a+1)+1; ll tmpid=A[p].id; swap(A[pos[x]].id,A[p].id); swap(pos[x],pos[tmpid]); A[p].a++; sgt2.modify(1,p,n,1); if(b[y]>0) sgt2.modify(1,0,b[y]-1,-1); b[y]--; }else{ ll p=bound(A[pos[x]].a); ll tmpid=A[p].id; swap(A[pos[x]].id,A[p].id); swap(pos[x],pos[tmpid]); A[p].a--; sgt2.modify(1,p,n,-1); sgt2.modify(1,0,b[y],1); b[y]++; } cout<<n*n-sgt2.t[1].mx<<"\n"; } return 0; } ```