DTOJ #5981

题意:

给定 n , M n,M n,M, { h n } \{h_n\} {hn},求最大的 k k k,满足 ∑ i = 1 n k − ( ( h i − 1 )   m o d   k + 1 ) < M \sum_{i=1}^n k-((h_i-1) \bmod k +1) < M i=1nk((hi1)modk+1)<M。其中 n ≤ 100 , M ≤ 1 0 11 , h i ≤ 1 0 9 n\leq100,M\leq 10^{11},h_i\leq 10^9 n100,M1011,hi109

题解:

虽然有非常简单的做法,但是这是还是要提一个思路清奇巧妙 考场上没想到sb做法 的做法。

考虑拆式子:
∑ i = 1 n k − ( ( h i − 1 )   m o d   k + 1 ) \sum_{i=1}^n k-((h_i-1) \bmod k +1) i=1nk((hi1)modk+1)
= n k − n − ∑ i = 1 n ( ( h i − 1 )   m o d   k ) =nk-n-\sum_{i=1}^n ((h_i-1) \bmod k) =nkni=1n((hi1)modk),令 x i = h i − 1 x_i=h_i-1 xi=hi1
= n k − n − ∑ i = 1 n ( x i   m o d   k ) =nk-n-\sum_{i=1}^n (x_i \bmod k) =nkni=1n(ximodk)
= n k − n − ∑ i = 1 n ( x i − k × ⌊ x i k ⌋ ) =nk-n-\sum_{i=1}^n (x_i-k \times \lfloor \frac{x_i}{k}\rfloor ) =nkni=1n(xik×kxi)
n k − n − ∑ i = 1 n x i + k × ∑ i = 1 n ⌊ x i k ⌋ ≤ M − 1 nk-n-\sum_{i=1}^n x_i+k \times \sum_{i=1}^n\lfloor \frac{x_i}{k}\rfloor \leq M-1 nkni=1nxi+k×i=1nkxiM1
即:
n k + k × ∑ i = 1 n ⌊ x i k ⌋ ≤ M − 1 + n nk+k\times \sum_{i=1}^n\lfloor \frac{x_i}{k}\rfloor \leq M-1+n nk+k×i=1nkxiM1+n
n + ∑ i = 1 n ⌊ x i k ⌋ ≤ ⌊ M − 1 + n k ⌋ n+\sum_{i=1}^n\lfloor \frac{x_i}{k}\rfloor \leq \lfloor \frac{M-1+n}{k} \rfloor n+i=1nkxikM1+n

然后对 ⌊ M − 1 + n k ⌋ \lfloor \frac{M-1+n}{k} \rfloor kM1+n 数论分块,每次就相当于求出在 [ l , r ] [l,r] [l,r] 内的 k k k 的最大值满足 ∑ i = 1 n ⌊ x i k ⌋ ≤ ⌊ M − 1 + n k ⌋ − n \sum_{i=1}^n\lfloor \frac{x_i}{k}\rfloor \leq \lfloor \frac{M-1+n}{k} \rfloor-n i=1nkxikM1+nn,该式子右边的值是固定的,所以我们只需要考虑左边即可,即 ∑ i = 1 n ⌊ x i k ⌋ \sum_{i=1}^n \lfloor \frac{x_i}{k} \rfloor i=1nkxi 如何快速求出区间内小于某个值得最靠右的位置。

考虑到 ⌊ x i k ⌋ \lfloor \frac{x_i}{k}\rfloor kxi 的值只有 x i \sqrt{x_i} xi 种,所以我们对于 ⌊ x i k ⌋ = j \lfloor \frac{x_i}{k}\rfloor=j kxi=j k k k 所在的区间都加上 j j j,然后我们只需要查询前 i i i 个位置小于等于某个值的最靠右的位置即可,可以将询问 [ l i , r i ] [l_i,r_i] [li,ri] 离线下来,然后扫描线维护,每次加入一个新的位置,用数据结构求出该点位置的值,然后去维护一个单调栈即可求出小于等于某个值的最靠右的位置,离散化一下即可。

考虑效率:预处理 ⌊ x i k ⌋ \lfloor \frac{x_i}{k}\rfloor kxi 的部分效率为 O ( n l o g n × m a x { x i } × m a x { x i } ) O(nlog_{n\times \sqrt{max\{x_i\}}}\times \sqrt {max\{x_i\}}) O(nlogn×max{xi} ×max{xi} ),询问部分效率为 O ( M ) × l o g n × m a x { x i } O(\sqrt{M}) \times log_{n\times \sqrt{max\{x_i\}}} O(M )×logn×max{xi} 约为 O ( x i × n l o g ( n × x i ) ) O(\sqrt{x_i} \times nlog(n\times \sqrt{x_i})) O(xi ×nlog(n×xi )),可能会 T L E TLE TLE,所以考虑分类,小于 S S S k k k 暴力 O ( n ) O(n) O(n) 遍历,大于 S S S k k k 采取上述策略。

暴力部分效率: O ( S n ) O(Sn) O(Sn)

另一个做法的效率: O ( n l o g × ⌊ x i S ⌋ ) O(nlog\times \lfloor \frac{x_i}{S}\rfloor) O(nlog×Sxi)

平衡一下效率: S n = n l o g × ⌊ x i S ⌋ Sn=nlog\times \lfloor \frac{x_i}{S}\rfloor Sn=nlog×Sxi
解得, S = 1 0 5 S=10^5 S=105

实测能过

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=105;
const int S=100000;
const int MAXN=4e6+10;
int n,h[N],mx;
LL m,ans;
struct Node{
	LL l,r,d;
}jia[MAXN],qu[MAXN];
int tot,tot_j,tot_q,c[MAXN<<1];
LL b[MAXN<<1];
vector<pair<int,int> > q[MAXN<<1];
int sta[MAXN<<1],st,val[MAXN<<1];
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int d){
	for(;x<=tot;x+=lowbit(x))c[x]+=d;
}
inline int ask(int x){
	int s=0;
	for(;x;x-=lowbit(x))s+=c[x];
	return s;
}
int main(){
	scanf("%d%lld",&n,&m);m+=n-1;
	for(int i=1;i<=n;++i)scanf("%d",&h[i]),h[i]--,m+=h[i],mx=max(mx,h[i]);
	sort(h+1,h+n+1);
	for(int i=1;i<=S;++i){
		LL s=1ll*n*i;
		for(int j=1;j<=n;++j)s+=1ll*(h[j]/i)*i;
		if(s<=m)ans=i;
	}
	if(mx<=S){
		printf("%lld\n",ans);
		return 0;
	}
	for(int i=1;i<=n;++i){
		if(h[i]<S)continue;
		for(int l=S+1,r;l<=h[i];l=r+1){
			r=h[i]/(h[i]/l);
			b[++tot]=l,b[++tot]=r;
			jia[++tot_j]=(Node){l,r,(h[i]/l)};
		}
	}
	for(LL l=S+1,r;l<=m;l=r+1){
		r=m/(m/l);
		b[++tot]=l,b[++tot]=r;
		qu[++tot_q]=(Node){l,r,(m/l)-n};
	}
	sort(b+1,b+tot+1);
	tot=unique(b+1,b+tot+1)-b-1;
	for(int i=1;i<=tot_j;++i){
		int L=lower_bound(b+1,b+tot+1,jia[i].l)-b;
		int R=lower_bound(b+1,b+tot+1,jia[i].r)-b;
		add(L,jia[i].d);
		add(R+1,-jia[i].d);
	}
	for(int i=1;i<=tot_q;++i){
		int L=lower_bound(b+1,b+tot+1,qu[i].l)-b;
		int R=lower_bound(b+1,b+tot+1,qu[i].r)-b;
		q[R].push_back(make_pair(L,qu[i].d));
	}
	for(int i=1;i<=tot;++i){
		val[i]=ask(i);
		while(st&&val[sta[st]]>=val[i])st--;
		sta[++st]=i;
		for(auto k:q[i]){
			int L=k.first,d=k.second;
			int l=1,r=st;
			while(l<r){
				int mid=(l+r+1)>>1;
				if(val[sta[mid]]<=d)l=mid;
				else r=mid-1;
			}
			if(st>=l&&val[sta[l]]<=d&&sta[l]>=L)ans=max(ans,b[sta[l]]);
		}
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值