2020ICPC江西C Charging(二分)
首先观察到答案具有单调性
其次,考虑如何check,一个朴素的想法就是枚举区间,之后判断覆盖这段区间的plan有几个,再取个min
这样做复杂度较高,考虑一下优化。
我们可以枚举右端点,之后把符合条件的左端点用树状数组维护,之后用一个指针去不断往前找到最远的符合覆盖大于二分值的点。
注意,这个指针不要回溯,这样才能保证复杂度,因为这样遍历的次数才是n,否则复杂度随着数据变化,并且这个指针本来就不需要回溯,因为你之前不满足答案,右端点左移,显然更不可能满足答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=3e5+10; vector<int> num[N]; int tr[N]; int n,m; int lowbit(int x){ return x&-x; } void add(int x,int c){ int i; for(i=x;i<N;i+=lowbit(i)){ tr[i]+=c; } } int sum(int x){ int res=0; for(int i=x;i;i-=lowbit(i)){ res+=tr[i]; } return res; } bool check(int mid){ int i; int now=n; memset(tr,0,sizeof tr); for(i=n;i>=1;i--){ for(auto x:num[i]){ add(x,1); } now=min(now,i); if(sum(now)<mid) continue; while(now>1&&sum(now-1)>=mid) now--; if(i-now+1>=mid) return true; } return false; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; int i; for(i=1;i<=m;i++){ int x,y; cin>>x>>y; num[y].push_back(x); } int l=0,r=min(n,m)+1; while(l<r){ int mid=l+r+1>>1; if(check(mid)) l=mid; else r=mid-1; } cout<<l<<endl; return 0; }
没有人不辛苦,只有人不喊疼