传送门
题意
开始有一个空序列s,一个变量c=0,接着从左往右依次将数组a中的数字放入s的尾部,每放一个数字就检测一次混乱度K,当混乱度k大于M时就清空序列并让c=c+1,K = Bi * Vi(1<=i<=k(序列总长度)的总和),Bi表示序列中第i小的数字,Vi是给定的非递减的数,输出每次加入序列后的变量c
分析
令当前左端点为L, 我们先找到一个最小的K, 使得[L,L+2K)这个区间混乱度超过M
然后右端点在[L+2K−1,L+2K)中二分即可
考虑每删去至少2K−1
个数, 所需要的时间复杂度最大为O(2KKlogN)
故总复杂度为O(Nlog2N)
trick
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
#pragma comment(linker, "/STACK:102400000,102400000")
inline void read(int &x){x=0; char ch=getchar();while(ch<'0') ch=getchar();while(ch>='0'){x=x*10+ch-48; ch=getchar();}}
int n;
ll m;
int a[300300],v[300300],Pow[25],temp[300300];
int ans[300300];
bool judge(int l,int r)
{
ll sum=0LL;
int cnt=0;
for(int i=l;i<=r;++i) temp[cnt++]=a[i];
sort(temp,temp+cnt);
for(int j=0;j<cnt;++j) sum+=(ll)temp[j]*v[j];
if(sum>m) return 1;return 0;
}
int erfen(int l,int left,int right)
{
while(left<right)
{
int mid=(left+right>>1);
if(judge(l,mid)) right=mid;else left=mid+1;
}
return left;
}
int check(int l,int n)
{
int left=l,right=l;
for(int i=0;;++i,right+=Pow[i])
{
if(right>n) right=n;
if(judge(l,right)) break;
left=right+1;
if(right==n) break;
}
return erfen(l,left,right);
}
int main()
{
Pow[0]=1;
F(i,1,24) Pow[i]=Pow[i-1]*2;
while(scanf("%d %lld",&n,&m)!=EOF)
{
F(i,0,n-1) scanf("%d",a+i);
F(i,0,n-1) scanf("%d",v+i);
mem(ans,0);
for(int i=0;i<n;)
{
int j=check(i,n);
ans[j]=ans[i==0?0:i-1]+1;
i=j+1;
}
for(int i=1;i<n;++i) ans[i]=max(ans[i],ans[i-1]);
for(int i=0;i<n;++i) printf("%d%c",ans[i],i==n-1?'
':' ');
}
return 0;
}