尺取
基本知识
形如:区间越大越容易符合条件/不符合条件。这样的问题,可以进行尺取。
对于一个 l l l ,找到第一个满足的 r r r ,显然 l , r l,r l,r 都是单调不减的。
例题练习1
例题1: n n n 个非负整数,求最小区间,使得区间和 > = S >=S >=S 。
分析: 越大越容易满足,对于每个 l l l ,找到第一个满足的 r r r
int ans=1e9,sum=0;
for(int i=1,j=0;i<=n;i++) {
while(j<n&&sum<s)sum+=a[++j];
if(sum<s)break;
ans=min(ans,j-i+1);
sum-=a[i];
}
例题2: n n n 个数 a i a_i ai ,值域为 [ 1 , m ] [1,m] [1,m] 。求最小区间,使得 a i a_i ai 在 [ 1 , m ] [1,m] [1,m] 中至少都出现一次。
分析: 同样是越大越容易满足,维护一个桶即可。
int ans=1e9,num=0;
for(int i=1,j=0;i<=n;i++){
while(j<n&&num<m){
count[a[++j]]++;
if(count[a[j]]==1)num++;
}
if(num<m)break;
ans=min(ans,j-i+1);
count[a[i]]--;
if(count[a[i]]==0)num--;
}
例题3: n n n 个数 a i a_i ai ,求 g c d > 1 gcd>1 gcd>1 的区间的最大长度。
分析: 越小越容易满足,每次找到一个最大且满足的即可。区间 g c d gcd gcd 可以用 s t st st 表快速维护。
例题4: n n n 个数 a i a_i ai ,求比 m i d mid mid 大的数, > = k >=k >=k 个,的区间个数
分析: 越大越容易满足,每次找到一个最小且满足的 r r r, r ∈ [ r , n ] r\in[r,n] r∈[r,n] 均满足。
例题练习2:
例题1: n n n 个数, a i a_i ai ,求区间和的绝对值与 k k k 相差最小为多少,并输出这个区间。
分析:
- 对前缀和排序,这样两个数的差值,就对应一个区间和。
- 而且符合单调性,两个数越远,差值越大。
- 每次找到第一个差值 > = k >=k >=k 的 r r r,以及第一个差值 < = k <=k <=k 的 r r r,即可。
例题2: 给定 n n n 个人的成绩 a i , b i a_i,b_i ai,bi ,每个人要么考到 a i a_i ai,要么考到 b i b_i bi 。定义及格为: > = >= >= n n n 个人中最高分的 p % p\% p% 。求最多多少人及格。
分析:
- 将所有成绩放一起,从小到大排序。
- 找到一个符合条件的区间,使得包含更多人的成绩。
- 显然区间越大,越不容易满足题意,因此可以尺取。
- 枚举 r r r ,左端点尺取到最后一个 l l l ,满足 v [ l ] > = v [ r ] × p % v[l]>=v[r]\times p\% v[l]>=v[r]×p% (并且 l , r l,r l,r 不属于同一个人成绩),然后检查 [ l , r ] [l,r] [l,r] 内有多少人的成绩即可。
分数规划
基本知识
模型:
形如: ∑ i = 1 n a i × w i ∑ i = 1 n b i × w i \frac{\sum_{i=1}^na_i \times w_i}{\sum_{i=1}^nb_i \times w_i} ∑i=1nbi×wi∑i=1nai×wi , w i ∈ 0 , 1 w_i\in 0,1 wi∈0,1 。要求最大化/最小化该式子。
求解:
二分 m i d mid mid ,去 c h e c k check check ∑ i = 1 n a i × w i ∑ i = 1 n b i × w i > = m i d \frac{\sum_{i=1}^na_i \times w_i}{\sum_{i=1}^nb_i \times w_i}>=mid ∑i=1nbi×wi∑i=1nai×wi>=mid 。
令第 i i i 个数为 a i − m i d × b i a_i-mid\times b_i ai−mid×bi ,问题就变成选择若干个数,使得权值和 > = 0 >=0 >=0 。
例题练习
例题1: 限制取 k k k 个数,最大化式子。
分析: 显然取最大 k k k 个。
例题2: 限制分母 > = v >=v >=v ,最大化式子。
分析: 将 b i b_i bi 看做重量, a i − m i d × b i a_i-mid\times b_i ai−mid×bi 看成价值,背包求 f [ V + ] f[V+] f[V+] 的最大值,即可。
int check(double mid){
for(int j=1;j<=V;j++)f[j]=-1e9;
for(int i=1;i<=n;i++){
for(int j=V;j>=0;j--){
int t=min(V,j+b[i]);//把f[V+]的最大值堆到f[i]身上
f[t]=max(f[t],f[j]+a[i]-mid*b[i]);
}
}
return f[V]>=0;
}
例题3: 给定 n n n 个点的一棵树,每个点有权值 a i , b i a_i,b_i ai,bi ,求所有环上 ∑ a i ∑ b i \frac{\sum a_i}{\sum b_i} ∑bi∑ai 最大为多少。
分析:
- 等价于 ∑ a i − b i × m i d > = 0 \sum a_i-b_i\times mid>=0 ∑ai−bi×mid>=0 ,等价于 ∑ b i × m i d − a i < = 0 \sum b_i\times mid-a_i<=0 ∑bi×mid−ai<=0
- 将 b i × m i d − a i b_i\times mid-a_i bi×mid−ai 当成点权,问题就变成:检查是否存在负环, s p f a spfa spfa 检查即可。
int check(double mid) {
queue<int>q;
for(int i=1;i<=n;i++){
dis[i]=0;
cnt[i]=vis[1]=1;
q.push(i);//由于图不一定连通,将所有点入队
}
while(!q.empty()) {
int u=q.front();
vis[u]=0;
for(int i=vex[u]; i; i=e[i].next) {
int v=e[i].v;
if(dis[v]>dis[u]+mid*b[u]-a[u]) {
dis[v]=dis[u]+mid*b[u]-a[u];
cnt[v]=cnt[u]+1;
if(cnt[v]>n)return 1;
if(!vis[v]) {
vis[v]=1;
q.push(v);
}
}
}
q.pop();
}
return 0;
}
例题4: 求区间最小平均值。
分析:
- ∑ i = l r a i r − l + 1 = ∑ i = l r a i ∑ i = l r 1 \frac{\sum_{i=l}^r a_i}{r-l+1}=\frac{\sum_{i=l}^r a_i}{\sum_{i=l}^r 1} r−l+1∑i=lrai=∑i=lr1∑i=lrai
- 二分 m i d mid mid ,问题转化为: ∑ i = l r a i − m i d < = 0 \sum_{i=l}^r a_i-mid<=0 ∑i=lrai−mid<=0 ,即最小字段和是否 < = 0 <=0 <=0 。
例题5: 求区间最大平均值。
分析: 同样的分析???不不不,区间最大平均值为区间最大值。