LOJ #6502「雅礼集训 2018 Day4」Divide - 构造 - dp

题解:考虑没有办法直接dp的原因是每次决策把某个点放入A或者B中不能确定产生了多少贡献。
因此考虑若能将 { w n } \{w_n\} {wn}适当排列,使得 ∀ i ∈ [ 1 , n ] ⇒ ( ∀ j ∈ [ 1 , i ) → w j + w i ≥ m ) ⋃ ( ∀ j ∈ [ 1 , i ) → w j + w i &lt; m ) \forall i\in[1,n]\Rightarrow\left(\forall j\in[1,i)\rightarrow w_j+w_i\ge m\right)\bigcup\left(\forall j\in[1,i)\rightarrow w_j+w_i&lt; m\right) i[1,n](j[1,i)wj+wim)(j[1,i)wj+wi<m),那么每次某个点 x x x加入A或者B,根据 w 1 + w x w_1+w_x w1+wx m m m的大小关系,可知产生的贡献为0或者另一部分点集的大小。
那么考虑构造这个排列。考虑若 w 1 + w n ≥ m w_1+w_n\ge m w1+wnm,那么可以直接把 w n w_n wn放到 w 1 … w n − 1 w_1\dots w_{n-1} w1wn1的右边。否则可以直接将 w 1 w_1 w1放到 w 2 … w n w_2\dots w_n w2wn的右边。递归处理即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define mod 1000000007
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=2010;int w[N],a[N],mx[N][N],f[N][N];
int main()
{
	int n=inn(),m=inn(),L=1,R=n;
	rep(i,1,n) w[i]=inn();sort(w+1,w+n+1);
	rep(i,1,n) (w[L]+w[R]>=m?a[i]=w[R--]:a[i]=w[L++]);
	reverse(a+1,a+n+1),mx[0][0]=0,f[0][0]=1;
	rep(i,1,n) rep(j,0,i)
	{
		int A,B,&t=f[i][j];
		if(a[1]+a[i]<m) A=(j>0?mx[i-1][j-1]:-1),B=(j<i?mx[i-1][j]:-1);
		else A=(j>0?mx[i-1][j-1]+i-j:-1),B=(j<i?mx[i-1][j]+j:-1);
		mx[i][j]=max(A,B);
		if(A>B) t=f[i-1][j-1];else if(A<B) t=f[i-1][j];
		else t=f[i-1][j-1]+f[i-1][j],(t>=mod?t-=mod:0);
	}
	int ans=-1,cnt=0;rep(i,0,n) ans=max(ans,mx[n][i]);
	rep(i,0,n) if(mx[n][i]==ans) cnt+=f[n][i],(cnt>=mod?cnt-=mod:0);
	return !printf("%d %d\n",ans,cnt);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值