LOJ#6502. 「雅礼集训 2018 Day4」Divide 题解

题目链接

考虑把数列\(w_i\)重新排列,使得新数列\(w_i\)满足对于任意 i , 所有的 \(w_{j(j<i)} + w_i \geq m\) 或者所有的 \(w_{j(j<i)} + w_i < m,\)然后\(O(n^2) DP\) 就容易了。

如何构造?

先把\(w_i\)从小到大排序。一开始答案序列\(ans\)为空。

如果\(w_1+w_n \geq m,\)那么对于所有 \(w_{i(i<n)},w_{i(i<n)}+w_n \geq m,\)可以把 \(w_n\) 放到\(ans\)的末尾,并删去 \(w_n\)

否则,对于所有\(w_{i(i<n)},w_{i(i<n)}+w_n < m,\)可以把 \(w_1\) 放到\(ans\)的末尾,并删去 \(w_1\)

那么就可以\(O(n\log n)\) 构造了。

复杂度\(O(n^2)\)

code :

#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &x){
	static char ch; x = 0,ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 2005,P = 1e9 + 7;
int n,m,a[N];
inline void build(){
	static int w[N];
	int i,len = n+1,l,r;
	for (i = 1; i <= n; ++i) w[i] = a[i]; sort(w+1,w+n+1);
	l = 1,r = n;
	while (l <= r){
		if (w[l] + w[r] >= m) a[--len] = w[r],--r;
		else a[--len] = w[l],++l;
	}
}
inline void upd(int &x,int v){ x = (x+v>=P)?(x+v-P):(x+v); }
int mx[N][N],f[N][N];
int main(){
	int i,j,v,t;
	int is;
	read(n),read(m);
	for (i = 1; i <= n; ++i) read(a[i]);
	build();
	for (i = 0; i <= n; ++i) for (j = 0; j <= n; ++j) mx[i][j] = -1;
	mx[1][0] = mx[1][1] = 0,f[1][0] = f[1][1] = 1;
	for (i = 1; i < n; ++i){
		is = a[i+1] + a[i] >= m ? 1 : 0;
		for (j = 0; j <= i; ++j) if (mx[i][j] > -1){
			v = f[i][j],t = mx[i][j] + is * (i-j);
			if (t == mx[i+1][j+1]) upd(f[i+1][j+1],v);
			else if (t > mx[i+1][j+1]) mx[i+1][j+1] = t,f[i+1][j+1] = v;
			t = mx[i][j] + is * j;
			if (t == mx[i+1][j]) upd(f[i+1][j],v);
			else if (t > mx[i+1][j]) mx[i+1][j] = t,f[i+1][j] = v;
		}
	}
	int Mx = 0,ans = 0;
	for (i = 0; i <= n; ++i)
		if (mx[n][i] > Mx) Mx = mx[n][i],ans = f[n][i];
		else if (mx[n][i] == Mx) upd(ans,f[n][i]);
	cout << Mx <<' ' <<ans << '\n';
	return 0;
}
posted @ 2020-09-07 19:22  srf  阅读(169)  评论(0编辑  收藏  举报