HDU 6231 K-th Number
题目大意:
给一个序列A,将其所有的大于等于K长度的子区间中,第K大的数取出来,放到B数组中。求B数组中第M大的数。
思路:
将所有的区间都取出来显然不行,时间复杂度爆炸。维护起来更奇怪。
可以考虑二分答案——先二分出这个B数组中第M大的数x,然后判断行不行——是否在原来的序列A中,有>=M个子区间,其>=x的数至少有K个。
我们知道这里是有单调性的——如果x增大,那么>=x的数至少有K个的区间数目就会变少;否则会增大。
接下来的问题是如何求出>=x的数至少有K个的子区间数目。
我们采用两个指针(l,r)的方法。不停地移动右指针,直到>=x的数有K个之后,移动左指针,直到左指针正好到从它原先位置开始的第一个>=x的数的位置上时——我们可以计算这特定K个数的贡献(具体请见代码qwq)
#include<bits/stdc++.h>
#define MAXN 100010
#define ll long long
using namespace std;
int n,T,maxx,cnt,cnt2;
ll sum;
int a[MAXN],prime[MAXN],cur[MAXN],not_prime[MAXN],Prime[MAXN];
inline void pre_solve(){
not_prime[1]=1;
for(int i=2;i<=100000;i++){
if(not_prime[i]==0){
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&prime[j]*i<=100000;j++){
not_prime[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
inline int ll calc(ll x){
ll cur_ans=0;
memcpy(cur,a,sizeof(int)*(n+1));
for(int i=1;i<=n;i++) cur[i]%=x;
sort(&cur[1],&cur[n+1]);
int l=1,r=n;
while(l<r){
while(cur[l]%x==0) l++;
while(cur[r]%x==0) r--;
int cur_sum=cur[l]%x;
cur_ans+=cur_sum;
while((cur[r]+cur_sum)>x){
cur_sum-=x-cur[r];
r--;
}
cur[r]+=cur_sum;
l++;
}
return cur_ans;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&T);
pre_solve();
while(T--){
sum=0,maxx=0,cnt2=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum+=a[i];
maxx=max(maxx,a[i]);
}
long long ans=sum-maxx;
int pre=2;
for(int i=pre;i<=(int)sqrt(sum);i++){
if(not_prime[i]==true||sum%i!=0) continue;
else{
Prime[++cnt2]=i;
pre=i;
while(sum%i==0) sum/=i;
}
}
// for(int i=1;i<=cnt2;i++) printf("%d ",Prime[i]); puts("");
for(int i=1;i<=cnt2;i++){
ans=min(ans,calc(Prime[i]));
}
if(sum!=1) ans=min(ans,calc(sum));
printf("%lld
",ans);
}
return 0;
}