K. Master of Sequence (转化&二分)

本文介绍了一种优化方法,通过将除法问题转化为求和来简化计算。利用dp数组高效判断条件,结合二分查找技术,解决了关于数组中特定条件元素数量的问题。时间复杂度为O(10^6 * logn + m),适用于处理大规模数据。
摘要由CSDN通过智能技术生成

K. Master of Sequence (转化&二分)

在这里插入图片描述
对于操作3:
肯定要考虑将除法转化为求和才能方便计算。
t = a i × k 1 + c 1 , b i = a i × k 2 + c 2 t=a_i\times k_1+c_1,b_i=a_i\times k_2+c_2 t=ai×k1+c1,bi=ai×k2+c2
⌊ t − b i a i ⌋ = k 1 − k 2 + [ c 1 − c 2 ] \lfloor\dfrac{t-b_i}{a_i}\rfloor\\=k_1-k_2+[c_1-c_2] aitbi=k1k2+[c1c2]

c 1 ≥ c 2 , [ c 1 − c 2 ] = 0 c_1\geq c_2 ,[c_1-c_2]=0 c1c2,[c1c2]=0
否则 [ c 1 − c 2 ] = − 1 [c_1-c_2]=-1 [c1c2]=1
因为 a i , b i a_i,b_i ai,bi已知,所以预处理出 k 2 k_2 k2的和。
式子可以转化为: S ( t ) ≥ k → ∑ ( k 1 − [ c 1 − c 2 ] ) ≥ ∑ k 2 + k S(t)\geq k\rightarrow \sum (k_1-[c_1-c_2])\ge \sum k2+k S(t)k(k1[c1c2])k2+k

对于判断所有 [ c 1 − c 2 ] = − 1 [c_1-c_2]=-1 [c1c2]=1,我们可以用一个二维数组 d p [ i ] [ j ] dp[i][j] dp[i][j]表示数组 a a a中等于 i i i b [ x ] % a [ x ] ≥ j b[x]\%a[x]\ge j b[x]%a[x]j的个数。
这样对于当前 t , i t,i t,i c n t = d p [ i ] [ t % i + 1 ] cnt=dp[i][t\%i+1] cnt=dp[i][t%i+1].

∑ ( k 1 − [ c 1 − c 2 ] ) = ∑ i = 1 1000 ( d p [ i ] [ 0 ] − d p [ i ] [ t % i + 1 ] ) \sum (k_1-[c_1-c_2])=\sum\limits_{i=1}^{1000} (dp[i][0]-dp[i][t\%i+1]) (k1[c1c2])=i=11000(dp[i][0]dp[i][t%i+1])

然后二分找到 t t t即可。

时间复杂度: O ( 1 0 6 × l o g n + m ) O(10^6\times logn+m) O(106×logn+m)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=998244353;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
#define lx x<<1
#define rx x<<1|1
int t,n,m;
int a[N],b[N]; 
int dp[1005][1005];//dp[i][j] a[i] 的b[i]/a[i]>=j的个数 
bool check(ll t,ll k){
	ll ans=0;
	for(int i=1;i<=1000;i++){
		ans+=t/i*dp[i][0];
		ans-=dp[i][t%i+1];
	} 
	return ans>=k;
}
ll fun(ll x){
	ll l=1,r=1e10;
	ll ans=0;
	while(l<=r){
		ll mid=(l+r)>>1;
		if(check(mid,x)){
			ans=mid;
			r=mid-1; 
		}
		else l=mid+1;
	}
	return ans;
} 
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		ll s2=0;//sum (b[i]/a[i])
		mst(dp,0);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]),s2+=b[i]/a[i],dp[a[i]][b[i]%a[i]]++;
		for(int i=1;i<=1000;i++)
			for(int j=i;j;j--)	dp[i][j-1]+=dp[i][j];
		while(m--){
			int op,x,y,k;
			scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&x,&y);
				s2-=b[x]/a[x];
				s2+=b[x]/y;
				for(int i=b[x]%a[x];~i;i--) dp[a[x]][i]--;
				for(int i=b[x]%y;~i;i--) dp[y][i]++;
				a[x]=y;
			}
			else if(op==2){
				scanf("%d%d",&x,&y);
				s2-=b[x]/a[x];
				s2+=y/a[x];
				for(int i=b[x]%a[x];~i;i--) dp[a[x]][i]--;
				for(int i=y%a[x];~i;i--) dp[a[x]][i]++;
				b[x]=y;
			} 
			else {
				scanf("%d",&k);
				printf("%lld\n",fun(k+s2));
			}
		} 
	}
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值