AcWing 109 天才ACM (倍增)

题目链接:https://www.acwing.com/problem/content/111/

倍增 + 二进制划分思想,
选定区间左端点\(L\), 不断向右扩展\(2\)的幂次长度,检验区间的合法性即可

复杂度怎么证明?
\(O(Nlog^{2}N)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 500010;

int K, n, m; ll T;
ll a[maxn], b[maxn];

ll squ(ll x){ return 1ll * x * x; } 

int solve(){
	ll res;
	int L = 1, ans = 0;
	while(L <= n){
		int p = 1, R = L, cnt, tot;
		for(;p > 0 && R < n;){
			res = 0, cnt = 0, tot = 0; // record current value;
			for(int i = L; i <= min(R + p, n); ++i) b[++cnt] = a[i];
			sort(b + 1, b + 1 + cnt); // sort the new elements 

			int i = 1, j = cnt;
			while(j - i >= 1 && tot < m * 2){
				res += squ(b[j] - b[i]);
				++i, --j; tot += 2;
			}
			if(res <= T){
				R = min(n, R + p);
				p <<= 1;
			}else{
				p >>= 1;
			}
		}
		L = R + 1; ++ans;
	}

	return ans;
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
	K = read();
	while(K--){
		n = read(), m = read(), T = read();
		for(int i=1;i<=n;++i) a[i] = read();
		printf("%d\n",solve());
	}
	return 0;
}
posted @ 2020-11-05 18:21  Tartarus_li  阅读(89)  评论(0编辑  收藏  举报