JZOJ 5921. 【NOIP2018模拟10.21】种花

1 篇文章 0 订阅
1 篇文章 0 订阅

题目

一个n的排列,如果满足所有的第i个位置不是p[i],则这个排列是合法的。
求所有的合法的排列的权值。
权值: ∑ j &gt; i , a j &lt; a i ( j − i ) ∗ ( a i − a j ) \sum_{j&gt;i,a_j&lt;a_i}(j-i)*(a_i-a_j) j>i,aj<ai(ji)(aiaj)
p[i]是一个全排列。
n<=5000

题解

多种方法
组合数。设 f [ x ] [ y ] f[x][y] f[x][y]表示x+y个格要填x+y个数。
有2种转移的方法:考虑最后一个格是有限制的,或最后一个格是没有限制的。
然后每对(i,j)都有自己独特的贡献
分类讨论
①i占p[j],j占p[i]。
②i占p[j],j不占p[i]或i不占p[j],j占p[i]
③i不占p[j]且j不占p[i]。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 5010
#define mo 1000000009
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,n,m,ans;
int f[N][3],p[N];
int jc[N],sum[N];
int a1,a2,a3,A;
int main(){
	scanf("%d",&n);
	fo(i,1,n)scanf("%d",&p[i]);
	f[0][0]=1;
	k=1;
	fo(i,1,n){
		f[i][0]=1ll*i*f[i-1][0]%mo;k=-k;
		f[i][0]=(f[i][0]+k+mo)%mo;
	}
	fo(i,1,2)f[0][i]=(1ll*f[0][i-1]*i)%mo;
	fo(i,1,n)fo(j,1,2)f[i][j]=(1ll*f[i-1][j]*i+j*f[i][j-1])%mo;
	A=0;
	fo(i,1,n-1)A=(A+1ll*(n-i)*i)%mo;
	fo(i,1,n)sum[i]=(sum[i-1]+i)%mo;
	fo(i,1,n-1)fo(j,i+1,n){
		a1=a2=a3=0;
		if(n>=2)a1=max(0,p[j]-p[i]);
		if(n>=3)a2=((sum[p[j]-1]+sum[n-p[i]])%mo-2*a1%mo+mo)%mo;
		if(n>=4){
			a3=((A-a1+mo)%mo-a2+mo)%mo;
			a3=(a3-sum[p[i]-1]+mo)%mo;
			a3=(a3-sum[n-p[j]]+mo)%mo;
			a3=(a3+max(0,p[i]-p[j]))%mo;
		}
		ans=(ans+1ll*(j-i)*((1ll*a1*f[n-2][0]+1ll*a2*f[n-3][1])%mo+1ll*a3*f[n-4][2]%mo)%mo)%mo;
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值