cogs2080Asm_Def排兵布阵解题报告

【题目描述】


医疗兵,滚过来!滚过来滚过来滚过来!”

“自己人!自己人!TM的别打了是自己人”

Asm.Def所带领的特种部队都是从全国各地最部队中挑选的,可以说全国最精锐的部队集合到了他这里。但是透明计算网络造反的突然,时间仓促,组建完部队后就立即出发了,并没有进行战术演练,配合相当生硬。

Asm.Def仔细研究了自己的阵型,找到了问题所在,比如说把突击部队和医疗兵位置布置的过远,不能及时吃血瓶。以及把前线部队放在了榴弹炮的攻击区域内。


Asm.Def的特种部队拥有k类兵种,第i类兵种有num(i)个单位。现在要求把他们排成一列。其中第i类兵种的最后一个人一定要排在第i+1类兵种的最后一个人的前面。现在问你有多少种排列方法。要求你输出对答案998244353(7×17×223+1,一个质数)取模后的值。

【输入格式】


第一行一个整数k,表示有k类兵种

第二行有k个整数,其中第i个整数表示num(i),为第i类兵种有num(i)人


【输出格式】

一行一个整数,为方案数对998244353取模后的值。

【样例输入】

3
2 2 1

【样例输出】

3

【提示】


样例解释

一共有下面3中排列的方法

1 2 1 2 3

1 1 2 2 3

2 1 1 2 3

数据范围

保证num(i)>0

40%的数据:所有兵种的总人数小于15

80%的数据:k <= 1000,所有兵种的总人数小于5000

100%的数据:k <= 100000, 所有兵种的总人数小于500000


【来源】

在此键入。

题解:留下每种兵种的最后一名 那么就成为了一个单调上升序列 考虑向这个序列里插入其他的士兵

那么第一种就只能插在序列的第一个元素之前

考虑其他的兵种 第i种兵的第j个人有sum(i-1)+j个位置可以去 

同理这种兵其他的也可以计算出来 最后在除去重复方案(除以全排列)

但数据范围太大 取过模后不支持除法运算

用乘法逆元计算除得的答案

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long MOD=998244353;
long long func[500010];
int num[100010];
inline void exgcd(long long a,long long b,long long &d,long long &x,long long &y){
	if(!b){
		d=a;
		x=1;
		y=0;
	}
	else {
		exgcd(b,a%b,d,y,x);
		y-=(a/b)*x;
	}
}
int main(){
	freopen("asm_formation.in","r",stdin);
	freopen("asm_formation.out","w",stdout);
	int n;
	scanf("%d",&n);
	int sum=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&num[i]);
		sum+=num[i];
	}
	func[1]=1;
	for(int i=2;i<=sum;i++)
		func[i]=((func[i-1]%MOD)*(i%MOD))%MOD;
	long long ans=1;
	int now=num[1];
	for(int i=2;i<=n;i++){
		if(num[i]!=1){
			long long res=func[num[i]-1]*func[now];
			res%=MOD;
			long long tl=func[num[i]-1+now];
			long long x,y,d;
			exgcd(MOD,res,d,x,y);
			long long r=res/d,M=MOD/d;
			if(x>0){
				long long k=x/r;
				k++;
				y+=k*M;
				y%=MOD;
			}
			if(y<0){
				long long k=y/M;
				k++;
				y+=k*M;
				y%=MOD;
			}
			y*=tl;
			y%=MOD;
			ans=ans*y;
			ans%=MOD;
			//ans=(ans+MOD)%MOD;
		}
		now+=num[i];
	}
	cout<<ans<<endl;
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值