膜拜(NDK1141)

膜拜

Time Limit:10000MS  Memory Limit:65536K
Total Submit:8 Accepted:5 

Description 

看完小品,就到了这次晚会的一个高潮,十八居士特地邀请了3位在NOI中夺银的附中神牛,让他们表演一
个节目。他们就是njn,pzy,tyt。但是同学们提议,神牛的人数太少,于是十八居士又邀请了福州一中
lxx, hzgd,l+q,xly三中wjx……甚至远在厦门的教主lhx。同学们对他们怀有深深地敬仰之情,所以他们要
膜拜这些神牛。神牛们按实力的顺序排成一个队伍,但是众所周知,神牛的特点就是爱装菜,这些神牛排
成的队伍往往不是真实的实力排序。现在已知神牛队伍的逆序对个数为m个,如(2,3,4,1)的逆序对
个数为3:(2,1),(3,1),(4,1)。 
所以十八居士定义p为这些神牛排列成逆序对为m的不同排列方式的个数mod 1000000007的值,而同学们膜
拜神牛的次数就是2^p mod 1000000007次。 

Input 

两个整数n,m。n表示神牛的个数。(n <= 1000,m <= 10000)

Output 

一个数,表示同学们膜拜神牛的次数。

Sample Input 

4 3

Sample Output 

64

Hint 

4个神牛排成混乱序列为3的队伍的排列方式共有6种:(1,4,3,2),(2,3,4,1),(2,4,1,3),(3,1,4,2),(3,2,1,4),(4,1,2,3),所以膜拜次数为2^6=64次


Source 


NOIdaokan



算法:DP

用来练习递推与组合数学的一道好题。开始就往数学那想去了,由于本人数学很菜因此做不出来,然后感
觉又有点递推但是不知道怎么推……
用f[i,j]表示前i个数构成j个逆序对的方案数,则:
f[i,j]=f[i-1,j]+f[i,j-1]-f[i-1,j-i]
其中f[i-1,j]表示i能得到1个逆序对,f[i,j-1]表示i没有得到1个逆序对,因为每个状态都包含了前面的
所有状态,而我们不难发现,有些状态是无法从前面的状态推导出来的,所以减f[i-1,j-i],因为第i项
最多只能获得i-1个逆序对,而j-i+i-1=j-1,是无论如何也组不成j个逆序对的,因此这个状态实际上并
不存在,所以我们还要减去这个状态。
如果用s表示m个数最多含有的逆序对的个数,那么s=m*(m-1) shr 1,不难发现这个式子中的每一项构成
了一个递增数列,每次递增的值就是i,因此每次我们都把循环上界+i。
另外需要注意的是,这道题不能使用二维的状态,内存使用为80+M,超过内存限制,用一维数组表示每个

状态。


二维版:

program NDK1141;

const
 maxn=1000;
 maxm=10000;

var
 n,m:longint;
 f:array [0..maxn,0..maxm] of int64;
 
function find(x:longint):int64;
var
 t:int64;
begin
 if x=0 then exit(1);
 t:=find(x shr 1);
 t:=(t*t) mod 1000000007;
 if odd(x) then t:=(t shl 1) mod 1000000007;
 exit(t);
end;

procedure init;
var
 i:longint;
begin
 readln(n,m);
 for i:=0 to n do f[i,0]:=1;
end;

procedure main;
var
 i,j,k:longint;
begin
 k:=1;
 for i:=2 to n do
  begin
   if k>m then k:=m;
   for j:=1 to k do f[i,j]:=(f[i-1,j]+f[i,j-1]-f[i-1,j-i]+1000000007) mod 1000000007;
   inc(k,i);
  end;
end;

begin
 assign(input,'NDK1141.in'); reset(input);
 assign(output,'NDK1141.out'); rewrite(output);
 
 init;
 main;
 writeln(find(f[n,m]));
 
 close(input); close(output);
end.

一维版:

program NDK1141;

const
 maxm=10000;

var
 n,m:longint;
 f,ff:array [-maxm..maxm] of int64;
 
function find(x:longint):int64;
var
 t:int64;
begin
 if x=0 then exit(1);
 t:=find(x shr 1);
 t:=(t*t) mod 1000000007;
 if odd(x) then t:=(t shl 1) mod 1000000007;
 exit(t);
end;

procedure init;
begin
 readln(n,m);
 f[0]:=1;
end;

procedure main;
var
 i,j,k:longint;
begin
 k:=1;
 for i:=2 to n do
  begin
   ff:=f;
   if k>m then k:=m;
   for j:=1 to k do f[j]:=(ff[j]+f[j-1]-ff[j-i]+1000000007) mod 1000000007;
   inc(k,i);
  end;
end;

begin
 assign(input,'NDK1141.in'); reset(input);
 assign(output,'NDK1141.out'); rewrite(output);
 
 init;
 main;
 writeln(find(f[m]));
 
 close(input); close(output);
end.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值