[CodeForces 1284C]New Year and Permutation(DP)

有些话、适合烂在心里 提交于 2020-01-11 15:13:08

在这里插入图片描述题意
若一个1 ~ n的排列中存在一个区间[L,R][L,R]使得这个区间内元素的最大值与最小值之差为RLR-L,则称其为一个片段,显然所有长度为1的区间均为一个片段。现在给定n与一个素数m,求1 ~ n的全排列中所有片段数之和对m取模的结果。
1n250000,108m1091≤n≤250000, 10^8≤m≤10^9
思路
这题是个DP,乍一看没有什么想法简单暴力显然行不通,求出每个排列然后依次统计必然是TLE的。
所以我们换一个思路,求出每种长度的片段数,再计算一下对于每种长度的片段,它在所有11 ~ nn的排列中有多少种放法,把它们乘起来求和就搞定了。
具体来说,对于11 ~ nn的排列,长度为ii片段,假设这个片段包含的元素为XX ~ X+i1X+i-1

  • 这个片段自身有ii!种排列方式
  • 剩下有nin-i个数,则这个片段可以放置在ni+1(n-i+1)个不同的位置(在它前面分别有0,1,…,nin-i个数)
  • 剩下的nin-i个数有(ni)!(n-i)!种排列方式
  • L的取值有11 ~ (ni+1)(n-i+1)(ni+1)(n-i+1)
    所以对于长度为ii片段,总共有inini+1ni+1i!*(n-i)!*(n-i+1)*(n-i+1)种放置方式,枚举考虑i=1,2......,ni=1,2......,n的情况求和即可。

然后我因为忘记取模wa了3发
保险起见,最好在每次乘积之后都进行一次取模运算。

AC代码

#include<stdio.h>
long long m,n;
long long mu[250005];
long long ans;
int main()
{
	scanf("%I64d%I64d",&n,&m);
	mu[0]=1;
	for(int i=1;i<=250000;i++)
	{mu[i]=mu[i-1]*(long long)i;mu[i]%=m;};
	for(int i=1;i<=n;i++)
	{
		ans+=((((mu[i]*mu[n-i]%m)*(n-((long long)i-1))%m)*(n-((long long)i-1)))%m);
		ans%=m;
	}
	printf("%I64d",ans);
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!