专栏文章

P14636 题解

P14636题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mimx4k6a
此快照首次捕获于
2025/12/01 16:59
3 个月前
此快照最后确认于
2025/12/01 16:59
3 个月前
查看原文
补题一小时过了,没有学题解的任何东西,因为我看不懂。
考虑原价不取到最大值的情况。如果一个物品可以取到肯定是最优的,因为性价比从大到小排序。但是如果一个物品没钱买可能就会出现问题。
考虑一种情况:按性价比从大到小选,到一个 wk=2w_k=2 的物品 kk,刚好剩余一块钱,kk 之前最近选了一个 wi=1w_i=1 的物品 iikk 之后有一个物品 jj 满足 wj=1w_j=1wi+wj<wkw_i+w_j<w_k。物品的性价比从大到小为 i,k,ji,k,j
按照小 R 的贪心思路,她会先选择 ii。接下来 kk 由于需要两块钱,但是只有一块钱没法选,所以小 R 会去选择 jj 并把钱花完。但是由于 wi+wj<wkw_i+w_j<w_k,用 kk 替换 i,ji,j 更优,就会导致贪心出错。
既然是由 i,j,ki,j,k 引起的贪心出错,那么我们枚举他们三个。注意 jj 可能没有,可以认为 j=0j=0
考虑 [1,j1],[j+1,i1],[i+1,k1],[k,n][1,j-1],[j+1,i-1],[i+1,k-1],[k,n] 这四个区间内的 ww 应如何选择。
[i+1,k1][i+1,k-1] 之间物品的权值是不确定的。因此枚举这个区间内 w=1w=1 的个数,设为 xx。方案数为 Cki1xC_{k-i-1}^x
上面这行文字是我赛时没有想到的,赛时以为 [i+1,k1][i+1,k-1] 之间全是 22sale2.in 调了一辈子,宝宝我真糖。好在想错了还有 m=2m=22020 分保底。
接着对于 [k,n][k,n] 之间的物品,还需要花 mx2m-x-2 元(ii 性价比比 kk 高,会先选择并花费 11 元)。即在 nkn-k 个物品中,每个物品有 1/21/2 的权值,问凑出 mx2m-x-2 的方案数。给权值 1-1,变成 mx2(nk)m-x-2-(n-k) 中选择 nkn-k11,答案为 Cmx2(nk)nkC_{m-x-2-(n-k)}^{n-k}
其他的在图中已经说清楚了,直接按照前文模拟,时间复杂度 O(Tn4)O(Tn^4),可以斩获比暴力多过一个测试点 66 的好成绩。
O(Tn4)O(Tn^4) codeCPP
ans=Pow(2,n);
for(int k=2;k<=n;k++)
{
	for(int i=1;i<k;i++)
	{
		if(a[i]*2<=a[k]||m-(n-k)<1) continue;
		for(int j=0;j<i;j++)
		{
			if(a[i]+a[j]>=a[k]) continue;
			for(int x=0;x<=k-i-1&&m-(n-k)-x>=2;x++)
			{
				if(m-(n-k)*2-x>2) continue;
				ans=(ans-Pow(2,j-1)*C[n-k][m-x-2-(n-k)]*C[k-i-1][x])%mod;
			}
		}
	}
}
枚举条件可以不用的,因为不合法的话组合数为 00
发现 jj 其实是没用的,唯一用处在于 2j12^{j-1}。双指针一下就可以去掉 jj 的枚举,做到 O(Tn3)O(Tn^3),理论上 5252,实测 9292
考虑去掉 xx 的枚举。如果你打了上周 ABC,会发现 F 题恰好有这个结论。引用一下原题第一篇题解的 Markdown。
范德蒙德卷积
t=0n(nt)(mkt)=(n+mk)\sum_{t=0}^{n}\binom{n}{t}\binom{m}{k-t}=\binom{n+m}{k}
证明:考虑一共有 n+mn+m 个物品,从中选 kk 个。这件事情等价于在 nn 个物品中先选 tt 个,再在剩下 mm 个物品中选 mkm-k 个。改变的仅仅是枚举顺序。
代入到 O(Tn4)O(Tn^4) 的代码中,就是:
ans=(ans-sum*C[n-j-1][m-2-(n-k)])%mod;
于是就可以做到 O(Tn2)O(Tn^2)
O(Tn2)O(Tn^2) codeCPP
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define N 5005
using namespace std;
int sub,n,m,ans,a[N],C[N][N];
int Pow(int x,int y)
{
	if(y<0) return 0;
	int res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	ans=Pow(2,n);
	for(int k=2;k<=n;k++)
	{
		int p=0,sum=1;
		for(int j=k-1;j>=1;j--)
		{
			while(p<n&&a[p+1]<a[k]-a[j]) p++,sum=sum*2%mod;
			if(a[j]*2<=a[k]||m-(n-k)<1||a[j]==a[k]) continue;
			if(m-2-(n-k)>=0&&m-2-(n-k)<=n-j-1) ans=(ans-sum*C[n-j-1][m-2-(n-k)])%mod;
		}
	}
	ans=(ans+mod)%mod;
	cout<<ans<<"\n";
	return;
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	C[0][0]=1;
	for(int i=1;i<=5000;i++)
	{
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	}
	int t;
	cin>>sub>>t;
	while(t--) solve();
	return 0;
}
事实上我的赛时:T2 调了三小时,操你妈的世界。

评论

0 条评论,欢迎与作者交流。

正在加载评论...