1. 程式人生 > 實用技巧 >HDU 6231 K-th Number

HDU 6231 K-th Number

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\n",ans);
    }
    return 0;
}