【NOI2009】 管道取珠

题目描述
管道取珠
【问题描述】
管道取珠是小X很喜欢的一款游戏。在本题中,我们将考虑该游戏的一个简单改版。游戏画面如图1所示:

(图1)
游戏初始时,左侧上下两个管道分别有一定数量的小球(有深色球和浅色球两种类型),而右侧输出管道为空。每一次操作,可以从左侧选择一个管道,并将该管道中最右侧的球推入右边输出管道。
例如,我们首先从下管道中移一个球到输出管道中,将得到图2所示的情况。

(图2)
假设上管道中有n个球, 下管道中有m个球,则整个游戏过程需要进行n + m次操作,即将所有左侧管道中的球移入输出管道。最终n + m个球在输出管道中从右到左形成输出序列。
爱好数学的小X知道,他共有C(n+m, n)种不同的操作方式,而不同的操作方式可能导致相同的输出序列。举个例子,对于图3所示的游戏情形:

(图3)
我们用A表示浅色球,B表示深色球。并设移动上管道右侧球的操作为U, 移动下管道右侧球的操作为D,则共有C(2+1,1)=3种不同的操作方式, 分别为UUD, UDU, DUU;最终在输出管道中形成的输出序列(从右到左)分别为BAB,BBA,BBA。可以发现后两种操作方式将得到同样的输出序列。
假设最终可能产生的不同种类的输出序列共有K种,其中第i种输出序列的产生方式(即不同的操作方式数目)有ai个。聪明的小X早已知道,(图4)

因此,小X希望计算得到(图5)

你能帮助他计算这个值么?由于这个值可能很大,因此只需要输出该值对1024523的取模即可(即除以1024523的余数)。
说明:文中C(n + m, n)表示组合数。组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。
【输入文件】
    输入文件ball.in第一行包含两个整数n, m,分别表示上下两个管道中球的数目。
    第二行为一个AB字符串,长度为n,表示上管道中从左到右球的类型。其中A表示浅色球,B表示深色球。
    第三行为一个AB字符串,长度为m,表示下管道中的情形。
【输出文件】
输出文件ball.out仅包含一行,即为图5除以1024523的余数。
【输入样例】
    2 1
        AB
    B
【输出样例】
    5
【样例说明】
样例即为文中(图3)。共有两种不同的输出序列形式,序列BAB有1种产生方式,而序列BBA有2种产生方式,因此答案为5。
【大致数据规模】
约30%的数据满足 n, m ≤ 12;
约100%的数据满足n, m ≤ 500


图1:

图2:

图3:

图4:

图5:

 

题解

 

解法1:搜索
 1 program ball;
 2 const std=1024523;
 3       maxf=100000;
 4 type
 5   ty1=^ty2;
 6   ty2=record
 7     s,num:int64;
 8     next:ty1;
 9   end;
10 
11 var
12   n,m,i:longint;
13   ans:int64;
14   pi:char;
15   p:ty1;
16   inn:array[0..2,0..500] of longint;
17   first:array[0..maxf] of ty1;
18 //===========================
19 procedure insert(s:int64);
20 var
21   k:longint;
22   p:ty1;
23 begin
24   k:=s mod maxf;
25   p:=first[k];
26   while p<>nil do
27   begin
28     if p^.s=s then
29     begin
30       inc(p^.num);
31       exit;
32     end;
33     p:=p^.next;
34   end;
35   new(p);
36   p^.s:=s;
37   p^.num:=1;
38   p^.next:=first[k];
39   first[k]:=p;
40 end;
41 //===========================
42 procedure find(nt,mt,k:longint; s:int64);
43 begin
44   if k=0 then
45   begin
46     insert(s);
47     exit;
48   end;
49   if nt=0 then find(nt,mt-1,k-1,s or (inn[2,mt]<<(k-1))) else
50   if mt=0 then find(nt-1,mt,k-1,s or (inn[1,nt]<<(k-1))) else
51   begin
52     find(nt,mt-1,k-1,s or (inn[2,mt]<<(k-1)));
53     find(nt-1,mt,k-1,s or (inn[1,nt]<<(k-1)));
54   end;
55 end;
56 //===========================
57 begin
58   assign(input,'ball.in'); reset(input);
59   assign(output,'ball.out'); rewrite(output);
60   readln(n,m);
61   for i:=1 to n do
62   begin
63     read(pi);
64     if pi='B' then inn[1,i]:=1;
65   end;
66   readln;
67   for i:=1 to m do
68   begin
69     read(pi);
70     if pi='B' then inn[2,i]:=1;
71   end;
72   find(n,m,n+m,0);
73   for i:=0 to maxf do
74   begin
75     p:=first[i];
76     while p<>nil do
77     begin
78       inc(ans,sqr(p^.num) mod std);
79       if ans>=std then dec(ans,std);
80       p:=p^.next;
81     end;
82   end;
83   writeln(ans);
84   close(input); close(output);
85 end.

 

解法2:动态规划
 1 program ball;
 2 uses math;
 3 const std=1024523;
 4       maxn=503;
 5 var
 6   n,m,i1,j1,i2,j2,val:longint;
 7   f:array[0..maxn,0..maxn,0..maxn] of longint;
 8   u,v:array[0..maxn] of char;
 9 //========================
10 procedure add(var a:longint; b:longint);    inline;
11 begin
12   inc(a,b);
13   if a>=std then dec(a,std);
14 end;
15 //========================
16 begin
17   assign(input,'ball.in'); reset(input);
18   assign(output,'ball.out'); rewrite(output);
19   readln(n,m);
20   for i1:=1 to n do read(u[i1]); readln;
21   for i2:=1 to m do read(v[i2]); readln;
22   f[0,0,0]:=1;
23   for i1:=0 to n do
24   for j1:=0 to m do
25   for i2:=0 to n do
26   begin
27     j2:=i1+j1-i2; val:=f[i1,j1,i2];
28     if val=0 then continue;        //这个优化很重要!——重视细节
29     if u[i1+1]=u[i2+1] then add(f[i1+1,j1,i2+1],val);
30     if u[i1+1]=v[j2+1] then add(f[i1+1,j1,i2],val);
31     if v[j1+1]=u[i2+1] then add(f[i1,j1+1,i2+1],val);
32     if v[j1+1]=v[j2+1] then add(f[i1,j1+1,i2],val);
33   end;
34   writeln(f[n,m,n]);
35   close(input); close(output);
36 end.
posted @ 2012-05-16 14:49  datam  阅读(390)  评论(0编辑  收藏  举报