尺取法+变通
所谓尺取法,是枚举区间的时候的一种优化思想。
尺取法通常适用于选取区间有一定规律,或者所选取的区间有一定的变化趋势的情况。
一般题目的尺取法比较普通,所以以下例题所用尺取法将比较特别。
尺取+逆元
https://ac.nowcoder.com/acm/contest/3005/C
l代表左端点,r代表右端点,当长度=k时,就比较答案,然后l++,r++,始终维持着长度=k,而因为是区间乘积,所以在l++、r++时,我们要把第 l 个元素除去,以及乘上第 r 个元素,而我们当前答案值已经是取模过后的数,所以为防止出现取模后为0的情况,把第 l 个元素除去要用到逆元的方式来处理。
#include <bits/stdc++.h> #define debug frelimiten("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 2e5+10; const int INF = 0x3f3f3f3f; const int mod = 998244353; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int n,k,i,l,r; ll sum,a[maxn],ans; int main() { n=read(),k=read(); for (i=1;i<=n;i++) a[i]=read(); sum=1; l=r=1; ans=-INF; while (r<=n) { if (a[r]) { sum=sum*a[r]%mod; if (r-l+1==k) { ans=max(ans,sum); sum=sum*qpow(a[l],mod-2)%mod; l++; } } else { l=r+1; sum=1; } r++; } cout<<ans<<endl; return 0; }
尺取+线段树
ICPC 2017 Asia Xian K. LOVER II
在此题之前有道简单版的B题,方法只用到了尺取法
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 2e5+5; const int INF = 0x3f3f3f3f; const int mod = 998244353; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int t,i,l; ll n,k,a[maxn],b[maxn],ans; int main() { t=read(); while (t--) { n=read(),k=read(); for (i=1;i<=n;i++) a[i]=read(); for (i=1;i<=n;i++) b[i]=read(); sort(a+1,a+1+n); sort(b+1,b+1+n); l=n; ans=0; for (i=1;i<=n;i++) { if (b[l]+a[i]>=k) { ans++; l--; } } cout<<ans<<endl; } return 0; }
而这题因为有个区间询问的附加问题,所以要在尺取法的基础上加上线段树。
怎么加呢?因为在最终的选择序列中,最少需要有 1 个男生与 a[1] 的和大于等于 k,有 2 个男生 与 a[2] 的 和大于等于 k,以此类推,所以我们把每位女生至少所能配对的男生数设为 -4,-3,-2,-1,意思是如果能全部匹配的话,那么,第1位女生一定会有4位男生的和大于等于 k,第2位女生一定会有3位男生的和大于等于 k……,所以当当前区间男生能和所有女生配对的话,那么所有值将会>=0。
我们计算每一位男生做为开头,然后在这名男生之后的男生中,要选多少个才能符合题目条件,所以我们设 l 做为某位男生的开头位置,r做为某位男生的最终位置,而以这位男生做为开头时,至少选到要到第 r位男生才能符合条件。而我们选下一位男生,即第l+1位男生时,先消除第l位男生的影响,然后再判断当前所有是否全部配对,若不全配对,就加入下一位男生r+1,计算这位男生的影响,直到符合题目条件。
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e6+10; const int INF = 0x3f3f3f3f; const int mod = 1e9+7; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} struct node { ll lazy,minn; }; node segment_tree[maxn*6]; struct segment { void push_up(int num) { segment_tree[num].minn=min(segment_tree[num<<1].minn,segment_tree[num<<1|1].minn); } void init(int num,int l,int r) { segment_tree[num].lazy=0; if (l==r) { segment_tree[num].minn=-l; return; } int mid=(l+r)/2; init(num<<1,l,mid); init(num<<1|1,mid+1,r); push_up(num); } void push_down(int num) { if (segment_tree[num].lazy==0) return; segment_tree[num<<1].lazy+=segment_tree[num].lazy; segment_tree[num<<1|1].lazy+=segment_tree[num].lazy; segment_tree[num<<1].minn+=segment_tree[num].lazy; segment_tree[num<<1|1].minn+=segment_tree[num].lazy; segment_tree[num].lazy=0; } void upgrade(int num,int l,int r,int want_zuo,int want_you,ll val) { if (want_zuo<=l && r<=want_you) { segment_tree[num].minn+=val; segment_tree[num].lazy+=val; return; } int mid=(l+r)/2; push_down(num); if (want_zuo<=mid) upgrade(num<<1,l,mid,want_zuo,want_you, val); if (want_you>mid) upgrade(num<<1|1,mid+1,r,want_zuo,want_you,val); push_up(num); } ll query(int num,int l,int r,int want_zuo,int want_you) { if (want_zuo<=l && r<=want_you) return segment_tree[num].minn; push_down(num); ll ans=INF; int mid=(l+r)/2; if (want_zuo<=mid) ans=min(ans,query(num<<1,l,mid,want_zuo,want_you)); if (want_you>mid) ans=min(ans,query(num<<1|1,mid+1,r,want_zuo,want_you)); return ans; } }tree; int t,m,n,k,i,a[maxn],b[maxn],R[maxn],pos,q,l,r; int main() { t=read(); while (t--) { n=read(),m=read(),k=read(); for (i=1;i<=n;i++) a[i]=read(); for (i=1;i<=m;i++) b[i]=read(); sort(a+1,a+1+n); tree.init(1,1,n); for (i=1;i<=m;i++) { R[i]=R[i-1]; while (segment_tree[1].minn<0) { R[i]++; if (R[i]>m) break; pos=lower_bound(a+1,a+1+n,k-b[R[i]])-a; if (pos<=n) tree.upgrade(1,1,n,pos,n,1); } pos=lower_bound(a+1,a+1+n,k-b[i])-a; if (pos<=n) tree.upgrade(1,1,n,pos,n,-1); } q=read(); while (q--) { l=read(),r=read(); if (R[l]<=r) cout<<1<<endl; else cout<<0<<endl; } } return 0; }