题解:P10685 [COTS/CETS 2024] 兔子 Zečevi
分析
首先二分答案
不妨假设兔子每次只能吃
这是左部点向右部点区间连边的二分图匹配问题,最近校内模拟赛里也出现过。记左部点的连边区间是
那么对数轴从左到右扫描线,则以上策略的实现是:当扫到
时间复杂度
代码
/*
author: honglan0301
Sexy_goodier _ xiaoqing
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cctype>
#include <queue>
#include <map>
#include <unordered_map>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <cmath>
#include <random>
#include <set>
#include <bitset>
#include <assert.h>
using namespace std;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define int long long
int n,m,flag,lim;
pair<int,int> x[100005],y[100005];
struct tree
{
int ls,rs,tag,len,sum;
}tree[10000005];
int cntd,rt;
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define l(x) tree[x].len
#define n(x) tree[x].sum
#define tg(x) tree[x].tag
#define md(x,y) ((x+y)>>1)
#define push_up(x) n(x)=n(ls(x))+n(rs(x))
#define cz(k,p) tg(p)+=k,n(p)+=1ll*k*l(p)
void push_down(int p)
{
if(tg(p))
{
if(!ls(p)) ls(p)=++cntd,l(ls(p))=(l(p)+1)/2; cz(tg(p),ls(p));
if(!rs(p)) rs(p)=++cntd,l(rs(p))=l(p)/2; cz(tg(p),rs(p)); tg(p)=0;
}
}
void cza(int l,int r,int x,int y,int k,int &p)
{
if(!p) p=++cntd,l(p)=r-l+1; if(l>=x&&r<=y) return cz(k,p),void(); int mid=md(l,r); push_down(p);
if(mid>=x) cza(l,mid,x,y,k,ls(p)); if(mid<y) cza(mid+1,r,x,y,k,rs(p)); push_up(p);
}
void czf(int l,int r,int k,int p)
{
if(l==r) return n(p)-=k,void(); int mid=md(l,r); push_down(p);
if(n(ls(p))<=k) czf(mid+1,r,k-n(ls(p)),rs(p)),ls(p)=0; else czf(l,mid,k,ls(p)); push_up(p);
}
int askm(int l,int r,int p)
{
if(l==r) return l; int mid=md(l,r); push_down(p);
if(n(ls(p))) return askm(l,mid,ls(p)); else return askm(mid+1,r,rs(p));
}
int sums=0;
void ins(pair<int,int> xx,int k)
{
if(xx.se>=k) return;
sums+=(k-1)-xx.se+1;
cza(0,lim,xx.fi+xx.se,xx.fi+k-1,1,rt);
}
void del(pair<int,int> xx,int k)
{
flag&=askm(0,lim,rt)>=xx.fi;
if(n(rt)<=xx.se) rt=0; else czf(0,lim,xx.se,rt);
}
void clr()
{
for(int i=1;i<=cntd;i++) ls(i)=rs(i)=n(i)=tg(i)=l(i)=0; cntd=rt=0;
}
int ck(int k)
{
int nzl=1,nzr=1; flag=1;
while(nzl<=n&&nzr<=m)
{
if(x[nzl].fi<=y[nzr].fi) ins(x[nzl++],k);
else del(y[nzr++],k);
}
while(nzl<=n) ins(x[nzl++],k);
while(nzr<=m) del(y[nzr++],k);
flag&=askm(0,lim,rt)==lim; clr(); return flag;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>x[i].fi>>x[i].se;
for(int i=1;i<=m;i++) cin>>y[i].fi>>y[i].se;
sort(x+1,x+n+1); sort(y+1,y+m+1);
int l=0,r=0;
for(int i=1;i<=n;i++) r+=x[i].se;
for(int i=1;i<=m;i++) r+=y[i].se; r/=n;
lim=r+2000000000ll;
while(l<=r)
{
int mid=(l+r)>>1;
if(ck(mid)) l=mid+1; else r=mid-1;
}
cout<<r<<endl;
}