5921. 【NOIP2018模拟10.22】种花

44 篇文章 0 订阅
15 篇文章 0 订阅

题目大意

在这里插入图片描述
因为找不到题面所以就勉为骑♂男C了一张下来

思路

因为题目要求的是最后的总和,所以可以分开每一对a[i]和a[j]来考虑,计算每一对的贡献
对于每一对数,考虑其位置和出现次数,可以分成几类出现次数相同的来讨论
那么每一类的贡献就是
(距离1+距离2+……)*出现次数*(a[i]-a[j])=距离之和*每一对的出现次数*(a[i]-a[j])
a[i]-a[j]可以放到最后再乘

分类讨论

设b[p[i]]表示p[i]在原序列的位置i,枚举a[i]和a[j]的具体数值,然后考虑ij在原序列上的位置(i<j 且 a[i]>a[j]且a[i]!=p[i]且a[j]!=p[j])
(下文为了方便直接用i表示a[i],j表示a[j],也就是i>j且i的位置在j前,同时上面的条件变成了i的位置!=b[i] 且 j的位置!=b[j])

先设f[n]表示当有n个数时的错排数(就是著名的错排公式
f [ n ] = ( n − 1 ) ( f [ n − 1 ] + f [ n − 2 ] ) f[n]=(n-1)(f[n-1]+f[n-2]) f[n]=(n1)(f[n1]+f[n2])
其中f[0]=1,f[1]=0

解释一下
对于每个新加入的数n,首先不能将其放在自己的位置上
所以就要和前面(n-1)个数中的一个交换
对于交换出来的数,如果直接将其放在n,那其它的数就不收影响,就是f[n-2]
如果不放在n,也就相当于这个数原来在n时的错排(因为错排就是每个数不能放在自己的位置,其它数不放在自己的位置上,交换的数也不放在n上,就相当于该数原本就在n上,但由于错排不能放在上面),也就是f[n-1]
所以
f [ n ] = ( n − 1 ) ( f [ n − 1 ] + f [ n − 2 ] ) f[n]=(n-1)(f[n-1]+f[n-2]) f[n]=(n1)(f[n1]+f[n2])
f [ 0 ] f[0] f[0]由于没有所以为1
f [ 1 ] f[1] f[1]由于不存在所以为0

i和j一定有至少一个在b[i]或b[j]上

b[i]<b[j]

那么就只可能j在b[i]上,i在b[j]上

j在b[i]上

在这里插入图片描述
那么j固定了,i可以在1~b[i]-1的任意一个位置(因为i的位置<j的位置,所以i不可能到b[i])
然后出现次数类似于错排公式,i可以在1~b[i]-1的任意一个位置上,将该位置上的数可以放到b[j]或不放b[j]
放的话就直接是f[n-3],不放的话就是f[n-2](等于原来在b[j]上但现在不能放在原位,就是n-2的错排)
所以该种情况的贡献为
( b [ i ] ∗ ( b [ i ] − 1 ) / 2 ) ∗ ( f [ n − 3 ] + f [ n − 2 ] ) (b[i]*(b[i]-1)/2)*(f[n-3]+f[n-2]) (b[i](b[i]1)/2)(f[n3]+f[n2])
(不用乘以b[i]-1是因为错排公式中每种情况的权值为1,所以要乘上去,但现在每种情况的权值为其长度,已在之前计算过。实际就是求每一对(ij都已固定)的出现次数)

i在b[j]上

在这里插入图片描述
那么j可以在b[j]+1~n的任意位置,大致同上
所以该种情况的贡献为
( 1 + n − b [ j ] ) ∗ ( n − b [ j ] ) / 2 ) ∗ ( f [ n − 3 ] + f [ n − 2 ] ) (1+n-b[j])*(n-b[j])/2)*(f[n-3]+f[n-2]) (1+nb[j])(nb[j])/2)(f[n3]+f[n2])

b[j]<b[i]

i在b[j]上,j在b[i]上

在这里插入图片描述
显然,其它n-2个数与ij没有关系
所以贡献就是
( b [ i ] − b [ j ] ) ∗ f [ n − 2 ] (b[i]-b[j])*f[n-2] (b[i]b[j])f[n2]

j在b[i]上,且i不在b[j]上

在这里插入图片描述
因为i不在b[j]上,所以长度总和=1~b[i]-1的和-(b[i]-b[j])(就是除掉i在b[j]上的情况)
然后次数和之前类似,也是f[n-3]+f[n-2]
所以贡献为
( b [ i ] ∗ ( b [ i ] − 1 ) / 2 − ( b [ i ] − b [ j ] ) ) ∗ ( f [ n − 3 ] + f [ n − 2 ] ) (b[i]*(b[i]-1)/2-(b[i]-b[j]))*(f[n-3]+f[n-2]) (b[i](b[i]1)/2(b[i]b[j]))(f[n3]+f[n2])

i在b[j]上,且j不在b[i]上

在这里插入图片描述
同上。
( ( 1 + n − b [ j ] ) ∗ ( n − b [ j ] ) / 2 − ( b [ i ] − b [ j ] ) ) ∗ ( f [ n − 3 ] + f [ n − 2 ] ) ((1+n-b[j])*(n-b[j])/2-(b[i]-b[j]))*(f[n-3]+f[n-2]) ((1+nb[j])(nb[j])/2(b[i]b[j]))(f[n3]+f[n2])

i和j都不在b[i]和b[j]上

求总和

考虑用容斥求总和。
首先可以O(n)算出总的长度和,再减去所有不合法的情况
不合法的情况是ij中有至少一个在b[i]或b[j]上的4种情况
然后发现b[i]~b[j]这一段被减了两次,要再加回去
设l为min(b[i],b[j]),r为max(b[i],b[j]),则总和就是
s u m − ( r ∗ ( r − 1 ) / 2 ) − ( ( 1 + n − l ) ∗ ( n − l ) / 2 ) − ( l ∗ ( l − 1 ) / 2 ) − ( ( 1 + n − r ) ∗ ( n − r ) / 2 ) + ( r − l ) sum-(r*(r-1)/2)-((1+n-l)*(n-l)/2)-(l*(l-1)/2)-((1+n-r)*(n-r)/2)+(r-l) sum(r(r1)/2)((1+nl)(nl)/2)(l(l1)/2)((1+nr)(nr)/2)+(rl)
然后实际和lr的顺序每多大关系,可以再优化(见最终代码)

那么对于每一对ij,考虑换出来的两个数的位置

有两个数在b[i]b[j]上

那么显然,就是f[n-4]
但因为换出来的数可以交换,所以实际上是
f [ n − 4 ] ∗ 2 f[n-4]*2 f[n4]2

有一个数在b[i]b[j]上

也很显然,就是f[n-3](另一个数不在另一个位置上)
但实际上有4(两个数*两个位置)种情况,贡献为
f [ n − 3 ] ∗ 4 f[n-3]*4 f[n3]4

一个数都不在b[i]b[j]上

由于这个问题过于哲♂学所以先不讨论

another problem

考虑另一个问题:
求在长度为n的错排下,有两个数不能放入另外两个位置中的任意一个
还是考虑容斥,设这两个数为i和j且ij都在其原来的位置上,则不合法的情况有
在这里插入图片描述
(X表示另一个数不在该位置)
由于是错排,首先加上f[n]
在这里插入图片描述
那么所有i和j在原位的情况都被考虑了
然后减去f[n-1]*2,也就是ij有一个在上面的情况
在这里插入图片描述
最后减去f[n-2],也就是两个都在上面的情况
在这里插入图片描述
所以最终的答案为
f [ n ] f[n] f[n] − - f [ n − 1 ] ∗ 2 f[n-1]*2 f[n1]2 − - f [ n − 2 ] f[n-2] f[n2]

原来的问题

因为原来的i和j都已被固定,所以就相当于上面长度为(n-2)时的问题
所以贡献为
f [ n − 2 ] − f [ n − 3 ] ∗ 2 − f [ n − 4 ] f[n-2]-f[n-3]*2-f[n-4] f[n2]f[n3]2f[n4]
真是奇♂妙

所以这一种情况的贡献为
( s u m − ( r ∗ ( r − 1 ) / 2 ) − ( ( 1 + n − l ) ∗ ( n − l ) / 2 ) − ( l ∗ ( l − 1 ) / 2 ) − ( ( 1 + n − r ) ∗ ( n − r ) / 2 ) + ( r − l ) ) ∗ ( f [ n − 4 ] ∗ 2 + f [ n − 3 ] ∗ 4 + ( f [ n − 2 ] − f [ n − 3 ] ∗ 2 − f [ n − 4 ] ) ) (sum-(r*(r-1)/2)-((1+n-l)*(n-l)/2)-(l*(l-1)/2)-((1+n-r)*(n-r)/2)+(r-l))*(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4])) (sum(r(r1)/2)((1+nl)(nl)/2)(l(l1)/2)((1+nr)(nr)/2)+(rl))(f[n4]2+f[n3]4+(f[n2]f[n3]2f[n4]))

最后要乘上(i-j)

code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define mod 1000000009
using namespace std;

long long f[5001];
long long b[5001];
int n;
long long i,j,l,r,ans,ans2,sum;

int main()
{
	freopen("derangement.in","r",stdin);
	freopen("derangement.out","w",stdout);
	
	scanf("%d",&n);
	fo(i,1,n)
	{
		scanf("%d",&j);
		b[j]=i;
	}
	
	if (n==2)
	{
		printf((b[1]<b[2])?"0\n":"1\n");
		return 0;
	}
	
	fo(i,1,n-1)
	sum=(sum+i*(n-i))%mod;
	
	f[0]=1;
	fo(i,2,n)
	f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
	
	fo(i,2,n)
	{
		fo(j,1,i-1)
		{
			ans2=0;
			
			if (b[i]<b[j])
			{
				ans2=(ans2+(b[i]*(b[i]-1)/2%mod)*(f[n-3]+f[n-2])%mod)%mod;
				ans2=(ans2+((1+n-b[j])*(n-b[j])/2%mod)*(f[n-3]+f[n-2])%mod)%mod;
			}
			else
			{
				ans2=(ans2+(b[i]-b[j])*f[n-2])%mod;
				ans2=(ans2+(b[i]*(b[i]-1)/2%mod-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
				ans2=(ans2+((1+n-b[j])*(n-b[j])/2%mod-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
			}
			if (n>=4)
			{
				l=min(b[i],b[j]);
				r=max(b[i],b[j]);
				
				ans2=(ans2+(sum-(r*(r-1)/2%mod)-((1+n-l)*(n-l)/2%mod)-(l*(l-1)/2%mod)-((1+n-r)*(n-r)/2%mod)+(r-l))%mod*
				(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4]))%mod)%mod;
			}
			
			ans=(ans+ans2*(i-j))%mod;
		}
	}
	
	printf("%lld\n",(ans+mod)%mod);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

code·最终版

每次求前后的长度和可以预处理,l和r也可以省略

#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define abs(x) ((x>0)?(x):-(x))
#define mod 1000000009
using namespace std;

long long A[5001];
long long B[5001];
long long f[5001];
long long b[5001];
int n;
long long i,j,l,r,ans,ans2,sum,S;

int main()
{
	freopen("derangement.in","r",stdin);
	freopen("derangement.out","w",stdout);
	
	scanf("%d",&n);
	fo(i,1,n)
	{
		scanf("%d",&j);
		b[j]=i;
	}
	
	if (n==2)
	{
		printf((b[1]<b[2])?"0\n":"1\n");
		return 0;
	}
	
	fo(i,1,n)
	{
		A[i]=b[i]*(b[i]-1)/2%mod;
		B[i]=(1+n-b[i])*(n-b[i])/2%mod;
	}
	
	fo(i,1,n-1)
	sum=(sum+i*(n-i))%mod;
	
	f[0]=1;
	fo(i,2,n)
	f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
	
	S=(f[n-4]*2+f[n-3]*4+(f[n-2]-f[n-3]*2-f[n-4]))%mod;
	
	fo(i,2,n)
	{
		fo(j,1,i-1)
		{
			ans2=0;
			
			if (b[i]<b[j])
			{
				ans2=(ans2+A[i]*(f[n-3]+f[n-2])%mod)%mod;
				ans2=(ans2+B[j]*(f[n-3]+f[n-2])%mod)%mod;
			}
			else
			{
				ans2=(ans2+(b[i]-b[j])*f[n-2])%mod;
				ans2=(ans2+(A[i]-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
				ans2=(ans2+(B[j]-(b[i]-b[j]))*(f[n-3]+f[n-2])%mod)%mod;
			}
			if (n>=4)
			ans2=(ans2+(sum-A[i]-B[i]-A[j]-B[j]+abs(b[i]-b[j]))*S)%mod;
			
			ans=(ans+ans2*(i-j))%mod;
		}
	}
	
	printf("%lld\n",(ans+mod)%mod);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值