题解:AT_abc200_e [ABC200E] Patisserie ABC 2

题目传送门


题目思路

暴力:

\(n^3\) 个蛋糕全部生成,然后按照要求排序。

时间复杂度:\(\operatorname{\Theta(n^3)}\)

正解:

因为题目中说蛋糕是在先按照三个属性值的和进行排序的,所以我们可以很容易想到枚举三个属性值的和(\(sum\)),把它们的方案数加起来,超过 \(K\) 时就是答案的属性值的和。那如何求方案数呢?我们可以用容斥原理加隔板法求解:

如果要把 \(sum\) 分成不为 \(0\) 的三份,则要在其中插 \(2\) 块板,而又有 \(sum-1\) 个空位,所以答案有 \(\binom{2}{sum-1}\)
其中有一些分法是不合理的(\(\scriptsize{\text{如当} N=2,sum=5 \text{时分成了} (3,1,1)}\)),我们要用容斥原理排除掉。这里有三个不合理的集合,关系如下图。

根据容斥原理的公式可以得知这几个集合的总数量为\(3\cdot \binom{2}{sum-n}+3\cdot \binom{2}{sum-2n}-\binom{2}{sum-3n}\)

整合得方案数为 \(\binom{2}{sum-1}-(\ 3\cdot \binom{2}{sum-n}+3\cdot \binom{2}{sum-2n}-\binom{2}{sum-3n}\ )=\binom{2}{sum-1}-3\cdot \binom{2}{sum-n}-3\cdot \binom{2}{sum-2n}+\binom{2}{sum-3n}\)

这时,我们得知了答案的属性值的和与它在和为 \(sum\) 时排在第几。然后我们暴力枚举美丽程度(\(first\)),把枚举到的美丽程度的方案数加起来,超过排名时就是答案。还是老问题,如何求方案数呢?

很明显的一条信息是当我们知道了答案的属性值的和以及美丽程度和美味程度(\(second\))后,人气(\(third\))就是固定的了。所以美味程度的取值范围就是总方案数。
因为美味程度的取值范围是 \(\max(sum-first-n,1)\)\(min(sum-first-1,n)\)。所以方案数是 \(\max(0,\min(sum-first-1,n)-\max(sum-first-n,1)+1)\)

之后我们直接枚举美味程度,统计合法的数量就可以求出答案。

时间复杂度:\(\operatorname{\Theta(n)}\)

代码

暴力:

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node{
	int x,y,z;
};
node a[10000000];
bool cmp(node x,node y){
	if(x.x+x.y+x.z!=y.x+y.y+y.z)	return x.x+x.y+x.z<y.x+y.y+y.z;
	if(x.x!=y.x)	return x.x<y.x;
	if(x.y!=y.y)	return x.y<y.y;
}
int t=-1,n,k;
signed main(){
	freopen("cake.in","r",stdin);
	freopen("cake.out","w",stdout);
	cin>>n>>k;	k--;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				a[++t]={i,j,k};
	sort(a,a+n*n*n,cmp);
	cout<<a[k].x<<' '<<a[k].y<<' '<<a[k].z<<'\n';
	return 0;
}

正解:

#include<bits/stdc++.h>
using namespace std;
#define IF_ON_ONLINEOJ true
#define IF_ON_LUOGU false
#define JIAO_HU_TI false
namespace fast{
#if IF_ON_LUOGU == false
#pragma GCC optimize("Ofast,unroll-loops,no-stack-protector")
#endif
#if JIAO_HU_TI == false
#define endl '\n'
#endif
#define il inline
#define re register
#define ri register int
#define ll long long
#if IF_ON_ONLINEOJ == true
    static char buf[100000],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#endif
    il void fast_out(){ios::sync_with_stdio(false);cout.tie(nullptr);}
    il int read(){
    	re int f=1;    re int x(0);    re char c=getchar();
        while(c>'9'||c<'0')	{if(c=='-')f=-f;c=getchar();}
        while(c<='9'&&c>='0')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x*=f;	return x;
    }
	istream& operator >>(istream& in,string& x){
	char ch=getchar();
	while(ch!=' '&&ch!='\n')	x+=ch,ch=getchar();
	return in;}
	istream& operator >>(istream& in,char& x){x=getchar();return in;}
}
#define int long long
using namespace fast;
int n,k;
il int C2x(int x){return x<3?0:(x-1)*(x-2)/2;}
il int tsumsum(int sum){return C2x(sum)-3*C2x(sum-n)+3*C2x(sum-2*n)-C2x(sum-3*n);}
il int tfirstsumsum(int first,int sum){
	return max(0ll,min(sum-first-1,n)-max(sum-first-n,1ll)+1);
}
signed main(){
	freopen("cake.in","r",stdin);
	freopen("cake.out","w",stdout);
	fast_out();	cin>>n>>k;
	ri sum;	for(sum=3;k>0;sum++)	k-=tsumsum(sum);
	k+=tsumsum(--sum);
	ri first;	for(first=1;k>0;first++)	k-=tfirstsumsum(first,sum);
	k+=tfirstsumsum(--first,sum);	sum-=first;	
	ri second;	for(second=1;k>0;second++)	k-=(sum-second<=n);	second--;
	cout<<first<<' '<<second<<' '<<sum-second<<endl;
	return 0;
}
posted @ 2025-04-18 13:21  _Charllote  阅读(42)  评论(0)    收藏  举报