把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF1726G A Certain Magical Party

题面传送门

不知道为什么做这道题的时候有一种修修补补的感觉

首先正着做看上去显然不太好做,我们考虑倒过来。

倒过来如果第一步是\(1\)那么是没有意义的,因此我们考虑第一步是\(0\),设最后到达的数是\(T\),则这个数会变成\(T-n+1\)

同时我们发现后面的最多一次性加\(n-2\),因此最后到达的数是固定的,也就是\(\min\limits_{i=1}^{n}{A_i}+n-1\)

然后我们发现,如果有两个\(x\)都是\(1\),那么必不可能有答案。因为如果其中一个加上去了,那么另一个加上去的值一定不相等。

最后,对于一个\(1\),要求小于它的数在它后面的等于\(T-x\)个。对于一个\(0\),要求小于等于它的数在它后面有\(x-\min\limits_{i=1}^{n}{A_i}\)个。如果合法,则答案乘上个数的阶乘。

但是过不去第三个样例呀?

我们发现,\(T\)\(1\)是不受限制,可以任意摆放的,因此还要乘一个排列数。

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=6e5+5,M=(1<<18)+5,K=1e5+5,mod=998244353,Mod=mod-1;const db eps=1e-5;const int INF=1e9+7;
int n,x,A[N],Mi=1e9,Mx,S1[N],S2[N],Ct;ll frc[N],Inv[N],ToT;
ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
ll C(int x,int y){return frc[x]*Inv[x-y]%mod;}
int main(){
	freopen("1.in","r",stdin);
	int i,j;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&A[i]),Mi=min(Mi,A[i]);Mx=Mi+n-1;
	for(i=1;i<=n;i++) if(A[i]>Mx){puts("0");return 0;}for(i=1;i<=n;i++) scanf("%d",&x),x?S2[A[i]]++:S1[A[i]]++;
	for(frc[0]=Inv[0]=i=1;i<=n;i++) frc[i]=frc[i-1]*i%mod,Inv[i]=mpow(frc[i]);int ZJAKIOI=0;for(i=1;i<=n;i++) if(A[i]^Mi) ZJAKIOI=1;if(!ZJAKIOI){printf("%lld\n",frc[n]);return 0;}
	for(i=Mi;i<Mx;i++) {if((Ct<Mx-i&&S1[i])||S2[i]>1) {puts("0");return 0;}Ct+=S1[i];if(Ct<i-Mi&&S2[i]){puts("0");return 0;}Ct+=S2[i];}
	ToT=frc[S1[Mx]];for(i=Mi;i<Mx;i++) ToT=ToT*frc[S1[i]]%mod*frc[S2[i]]%mod;printf("%lld\n",ToT*C(n,S2[Mx])%mod);
}
posted @ 2022-09-12 16:49  275307894a  阅读(41)  评论(2编辑  收藏  举报
浏览器标题切换
浏览器标题切换end