P4477 【[BJWC2018]基础匹配算法练习题】
谔谔我,写了好久,还重构了一次,主要是本人对于常数的估算出了严重的问题。
看见最大流,肯定要先搞个性质出来才能做(如果您暴力见图跑网络流还能AC,您就差不多世界闻名了qwq)
我看完题30秒大概看出来这样一个东西(以下默认从B往A连边):大权值的点能连的点小权值一定也连了,小权值的点能连的点大权值不一定连,所以让小权值去连权值尽量大的点
这个贪心显然没毛病,而且挺好用的qwq
一、复杂度正确并且很好想的算法
PS:如果要看能AC的方法,那么请看“二”
显然,找到能连并且最大的点就是前驱,搞个平衡树。
另外,如果区间缩短,放出来的那些权值貌似不是很好处理,但是伸长的时候可以找到最大的没用过的,更新答案,所以回滚一下。
具体实现:
- 每 \(\sqrt{m}\) 次把 \(a\) 插入一颗初始空的平衡树
- 区间伸长的时候找到平衡树里最大的没用过的,如果找到,那么从平衡树里删除,\(++ans\)。
- 扩展左端点的时候搞个数组(或 \(vector\)) 记录删去了哪些节点,回滚时插回去
复杂度:
插入节点 \(\sqrt{m}\cdot n \log n\) ,左右端点扩展并找前驱更新答案 \(\sqrt{m} \cdot n\log n\) ,左端点回滚最多 \(\sqrt{m}\cdot \log n\) ,总复杂度 \(\sqrt{m}\cdot n\log n\)
于是就解决了这题
交上去会发现 \(TLE50pts\) 。写假了???并没有。本地极限随机数据跑了 \(40s\) ,不断删去函数发现了瓶颈: \(\sqrt{m}\cdot n\) 次插入花了 \(20s\)
如果您还是不知道为啥TLE,我就再说的直白一点:平衡树常数上天了!!!
代码就不放了,要的私信(或者您AC了看我的提交记录qwq,50分的都是)
接着我重构了/dk
二、能AC的方法
还是原来那个结论:让小权值去连权值尽量大的点。
换一个思路,如果排了序,那么每个点能连的点是左端点为 \(1\) ,右端点可以二分预处理的一段区间。
于是考虑搞一颗线段树维护区间加区间最小值。每次加入一个点就区间加一,删除则区间减一,最后查询全局最小值即可。
注意每个节点的初始值是区间左端点,因为到这个叶子节点最多只能连这个点下标个点,然后非叶子节点取min,最终就是左端点。
#pragma GCC optimize(3)
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rint register int
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
inline int rd() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x*f;
}
const int N=160000;
const int M=60000;
int n,m,z,q;
int size,bel[M];
int a[N],b[M],c[M],ans[M];
int val[M<<2],tag[M<<2];
struct Q {
int l,r,id;
}que[M];
bool cmp(const Q &a,const Q &b) {
return bel[a.l]!=bel[b.l]?bel[a.l]<bel[b.l]:bel[a.l]&1?bel[a.r]<bel[b.r]:bel[a.r]>bel[b.r];
}
int min(const int &a,const int &b) {return a<b?a:b;}
int max(const int &a,const int &b) {return a>b?a:b;}
#define lc (p<<1)
#define rc (p<<1|1)
void build(int l,int r,int p) {
val[p]=l;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,lc),build(mid+1,r,rc);
}
void pushdown(int p,int l,int r) {
if(!tag[p])return;
tag[lc]+=tag[p];
val[lc]+=tag[p];
tag[rc]+=tag[p];
val[rc]+=tag[p];
tag[p]=0;
}
void update(int ql,int qr,int l,int r,int p,int k) {
if(ql<=l&&r<=qr) {
tag[p]+=k,val[p]+=k;
return;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(mid>=ql)update(ql,qr,l,mid,lc,k);
if(mid<qr)update(ql,qr,mid+1,r,rc,k);
val[p]=min(val[lc],val[rc]);
}
void add(int x) {if(c[x])update(1,c[x],1,m,1,1);}
void pop(int x) {if(c[x])update(1,c[x],1,m,1,-1);}
signed main() {
n=rd(),m=rd(),z=rd(),size=n/sqrt(m*3/2);
for(rint i=1;i<=n;++i)a[i]=rd();sort(a+1,a+n+1);
for(rint i=1;i<=m;++i)b[i]=rd();
for(rint i=1;i<=m;++i)c[i]=upper_bound(a+1,a+n+1,z-b[i])-a-1;
for(rint i=1;i<=m;++i)bel[i]=(i-1)/size+1;
q=rd();build(1,m,1);
for(rint i=1;i<=q;++i)que[i].l=rd(),que[i].r=rd(),que[i].id=i;
sort(que+1,que+q+1,cmp);
for(rint i=1,l=1,r=0;i<=q;++i) {
while(l<que[i].l)pop(l++);
while(l>que[i].l)add(--l);
while(r<que[i].r)add(++r);
while(r>que[i].r)pop(r--);
ans[que[i].id]=val[1]-1;
}
for(rint i=1;i<=q;++i)printf("%d\n",ans[i]);
return 0;
}