膜拜
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
算法: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,超过内存限制,用一维数组表示每个
一维版:
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.
624

被折叠的 条评论
为什么被折叠?



