1. 所有公共子序列问题
(allcs.pas/c/cpp)
★问题描述:
一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序
列X= x 1 x 2 ... x m ,则另一序列Z= z 1 z 2 ... z k 是X的子序列是指存在一个严格递增下标序列
i 1 , i 2 ,..., i k 使得对于所有j=1,2,...,k有 z j x ij 。例如,序列Z=GACT是序列X= GCTACT的子
序列,相应的递增下标序列为1,4,5,6。给定2个序列X和Y,当另一序列Z既是X的子序列又是
Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X= GCTACT,Y= GATCCT,序列
TT是X和Y的一个公共子序列,序列GACT也是X和Y的一个公共子序列。注意对于任何给定
的序列X和Y,空序列总是它们的一个公共子序列。
所有公共子序列问题是要求对于给定的2个序列X= x 1 x 2 ... x m 和Y= y 1 y 2 ... y n ,找出X
和Y的所有不同的公共子序列。
★编程任务:
对于给定的2个序列X= x 1 x 2 ... x m 和Y= y 1 y 2 ... y n ,根据输入数据的要求,找出它们所
有不同的公共子序列,或计算出它们有多少个不同的公共子序列。
★数据输入:
输入文件名为allcs.in。
文件的第一行有2个正整数m和n, (1<=m,n<=3010),分别表示2个输入序列X和Y的长度。
接下来的2行分别给出输入序列X= x 1 x 2 ... x m 和Y= y 1 y 2 ... y n 。其中序列中每个元素均为26
个英文大小写字母。文件的最后一行给出一个非负整数k。k的值为1时,表示计算结果要输
出X和Y的所有不同的公共子序列,以及X和Y有多少个不同的公共子序列。k的值为0时,表
示计算结果只要输出X和Y有多少个不同的公共子序列。
★结果输出:
输出文件名为allcs.out。
将计算出的X和Y的所有不同的公共子序列,或X和Y有多少个不同的公共子序列输出到
文件中。当k=1时,先输出X和Y的所有不同的公共子序列,每行输出1个公共子序列,按字
典序从小到大输出。最后输出不同的公共子序列的个数。当k=0时,只要输出不同的公共子
序列的个数。输入示例1
6 6
GCTACT
GATCCT
1输出示例1
26
A
AC
ACT
AT
C
CC
CCT
CT
G
GA
GAC
GACT
GAT
GC
GCC
GCCT
GCT
GT
GTC
GTCT
GTT
T
TC
TCT
TT
26输入示例2
6 6
GCTACT
GATCCT
0输出示例2
26
根连向每个字母第一次出现的位置,每个位置的儿子分别连向它之后每个字母第一次出现的位置.这貌似就是传说中的序列自动机.
在两个序列自动机同时dfs.需要输出序列则直接dfs并输出即可.
不需要输出序列时,记忆化搜索.但是由于答案很大,需要高精.
std又WA又T又RE,我也没有什么办法.
70分代码.
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=3017;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 char a[N],b[N],prt[N];
18 int n,m,K,ch1[N][52],ch2[N][52],ls[52];
19
20 template<typename T> void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
23 if(ch=='-') f=-1,ch=getchar();
24 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
25 }
26
27 void make_it(char s[],int n,int ch[][52]) {
28 memset(ls,0,sizeof(ls));
29 For(i,1,n) {
30 int c=(s[i]>='a'&&s[i]<='z')?s[i]-'a':26+s[i]-'A';
31 if(!ls[c]) ch[0][c]=i;
32 ls[c]=i;
33 }
34 memset(ls,0,sizeof(ls));
35 Rep(i,n,1) {
36 int c=(s[i]>='a'&&s[i]<='z')?s[i]-'a':26+s[i]-'A';
37 For(j,0,51) ch[i][j]=ls[j];
38 ls[c]=i;
39 }
40 }
41
42 int dp[N][N],tot;
43 struct G {
44 int len;
45 LL d[26];
46 };
47
48 const LL bs=1e17;
49 G operator +(const G&A,const G&B) {
50 G rs;
51 For(i,0,25) rs.d[i]=0;
52 rs.len=max(A.len,B.len)+1;
53 LL pr=0;
54 For(i,1,rs.len) {
55 rs.d[i]=A.d[i]+B.d[i]+pr;
56 if(rs.d[i]>=bs) pr=1,rs.d[i]-=bs;
57 else pr=0;
58 }
59 while(rs.d[rs.len]==0) rs.len--;
60 return rs;
61 }
62
63 G ans,g[500007];
64
65 G dfs(int x,int y) {
66 if(dp[x][y]!=-1) return g[dp[x][y]];
67 dp[x][y]=++tot;
68 G rs; rs.len=1;
69 For(i,0,25) rs.d[i]=0; rs.d[1]=1;
70 For(i,0,51) if(ch1[x][i]&&ch2[y][i]) {
71 rs=rs+dfs(ch1[x][i],ch2[y][i]);
72 }
73 g[dp[x][y]]=rs;
74 return rs;
75 }
76
77 void PT(G a) {
78 printf("%lld",a.d[a.len]);
79 Rep(i,a.len-1,1) {
80 if(a.d[i]==0) printf("0000000000000000");
81 else {
82 LL tp=a.d[i];
83 while(tp*10<bs) { printf("0"); tp*=10; }
84 printf("%lld",a.d[i]);
85 }
86 } printf("
");
87 }
88
89 int anstot;
90 void print(int x,int y,int pos) {
91 prt[pos]=a[x];
92 prt[pos+1]=0;
93 puts(prt); anstot++;
94 For(i,0,51) if(ch1[x][i]&&ch2[y][i])
95 print(ch1[x][i],ch2[y][i],pos+1);
96 }
97
98 #define DEBUG
99 int main() {
100 #ifdef DEBUG
101 freopen("allcs.in","r",stdin);
102 freopen("allcs.out","w",stdout);
103 #endif
104 read(n); read(m);
105 scanf("%s",a+1);
106 make_it(a,n,ch1);
107 scanf("%s",b+1);
108 make_it(b,m,ch2);
109 read(K);
110 memset(dp,-1,sizeof(dp));
111 if(!K) {
112 ans.len=1;
113 For(i,0,25) ans.d[i]=0; ans.d[1]=1;
114 For(i,0,51)
115 if(ch1[0][i]&&ch2[0][i])
116 ans=ans+dfs(ch1[0][i],ch2[0][i]);
117 PT(ans);
118 }
119 if(K) {
120 puts("");
121 For(i,0,51)
122 if(ch1[0][i]&&ch2[0][i])
123 print(ch1[0][i],ch2[0][i],0);
124 printf("%d
",anstot+1);
125 }
126 return 0;
127 }
2. 建筑师
(building.pas/c/cpp)
★问题描述:
小Z是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建n个建筑,
每个建筑的高度是1到n之间的一个整数。小Z有很严重的强迫症,他不喜欢有两个建筑的高
度相同。另外小Z觉得如果从最左边(所有建筑都在右边)看能看到A个建筑,从最右边(所
有建筑都在左边)看能看到B个建筑,这样的建筑群有着独特的美感。现在,小Z想知道满足
上述所有条件的建筑方案有多少种?
如果建筑i的左(右)边没有任何建造比它高,则建筑i可以从左(右)边看到。两种方案不
同,当且仅当存在某个建筑在两种方案下的高度不同。
★数据输入:
输入文件名为building.in。
第一行一个整数 T,代表 T 组数据。
接下来T行,每行三个整数n,A,B。
★结果输出:
输出文件名为building.out。
9
对于每组数据输出一行答案 mod ( 10 ^9+ 7 ) 。输入示例
2
3 2 2
3 1 2输出示例
2
1★数据范围:
10% : 1 <= n <= 10
20% : 1 <= n <= 100
40% : 1 <= n <= 50000,1 <= T <= 5
100% :1 <= n <= 50000,1 <= A, B <= 100,1 <= T <= 200000。
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cstdio>
7 #include<cmath>
8 const int N=5e4+7;
9 const int mod=1e9+7;
10 typedef long long LL;
11 using namespace std;
12 LL dp[N][207],C[207][207];
13 int T,n,a,b;
14
15 template<typename T> void read(T &x) {
16 char ch=getchar(); T f=1; x=0;
17 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
18 if(ch=='-') f=-1,ch=getchar();
19 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
20 }
21
22 #define DEBUG
23 int main() {
24 #ifdef DEBUG
25 freopen("building.in","r",stdin);
26 freopen("building.out","w",stdout);
27 #endif
28 dp[1][1]=1;
29 for(int i=2;i<=50000;i++)
30 for(int k=1;k<=min(200,i);k++)
31 dp[i][k]=(dp[i-1][k]*(i-1)%mod+dp[i-1][k-1])%mod;
32 for(int i=0;i<=200;i++) C[i][0]=1;
33 for(int i=1;i<=200;i++)
34 for(int j=1;j<=i;j++)
35 C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
36 read(T);
37 while(T--) {
38 read(n); read(a); read(b);
39 LL ans=dp[n-1][a+b-2];
40 ans=(ans*C[a+b-2][a-1])%mod;
41 printf("%lld
",ans);
42 }
43 return 0;
44 }
45 /*
46 3 2 2
47 2 1
48 */
3. 神秘数
(mystic.pas/c/cpp)
★问题描述:
一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数。例如
S={1,1,1,4,13},
1 = 1
2 = 1+1
3 = 1+1+1
4 = 4
5 = 4+1
6 = 4+1+1
7 = 4+1+1+1
8无法表示为集合S的子集的和,故集合S的神秘数为8。
现给定n个正整数a[1]..a[n],m个询问,每次询问给定一个区间[l,r](l<=r),求由
a[l],a[l+1],...,a[r]所构成的可重复数字集合的神秘数。
★编程任务:
求出每个查询的结果。
★数据输入:
输入文件名为mystic.in。
第一行一个整数n,表示数字个数。
第二行n个整数,从1编号。
第三行一个整数m,表示询问个数。
以下m行,每行一对整数l,r,表示一个询问。
★结果输出:
输出文件名为mystic.out。
对于每个询问,输出一行对应的答案。输入示例
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5输出示例
2
4
8
8
8★数据范围:
对于 10%的数据点,n,m <= 10
对于 30%的数据点,n,m <= 1000
对于 60%的数据点,n,m <= 50000
对于 100%的数据点,n,m <= 100000,∑a[i] <= 10
十分奥妙的一道题.
求一段区间的神秘数,先把区间内的数从小到大排序.
依次加入每个数x,若当前神秘数为ans
若x>ans+1,神秘数仍为ans
否则神秘数变为ans+x
排序后,找到第一个大于它前面所有数的和+1的数,它前面所有数的和+1即为神秘数.
那么求区间l,r的神秘数,先将当前神秘数ans设为1,查询区间内小于等于ans的数的和sum,若sum>ans,则ans=sum+1
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=100007;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 int n,m,a[N],sz;
18
19 template<typename T> void read(T &x) {
20 char ch=getchar(); x=0; T f=1;
21 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
22 if(ch=='-') f=-1,ch=getchar();
23 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
24 }
25
26 int tot,rt[N],sg[N*71],ls[N*71],rs[N*71];
27 #define lc ls[x]
28 #define rc rs[x]
29 #define mid ((l+r)>>1)
30 void update(int &x,int l,int r,int pos,int last) {
31 x=++tot;
32 sg[x]=sg[last]+pos;
33 lc=ls[last];
34 rc=rs[last];
35 if(l==r) return;
36 if(pos<=mid) update(lc,l,mid,pos,ls[last]);
37 else update(rc,mid+1,r,pos,rs[last]);
38 }
39
40 int qry(int x,int l,int r,int ql,int qr) {
41 if(!x) return 0;
42 if(l>=ql&&r<=qr) return sg[x];
43 if(qr<=mid) return qry(lc,l,mid,ql,qr);
44 if(ql>mid) return qry(rc,mid+1,r,ql,qr);
45 return qry(lc,l,mid,ql,qr)+qry(rc,mid+1,r,ql,qr);
46 }
47
48 #define DEBUG
49 int main() {
50 #ifdef DEBUG
51 freopen("mysti.in","r",stdin);
52 freopen("mysti.out","w",stdout);
53 #endif
54 read(n);
55 For(i,1,n) { read(a[i]); sz=max(sz,a[i]); }
56 For(i,1,n) update(rt[i],1,sz,a[i],rt[i-1]);
57 read(m);
58 For(i,1,m) {
59 int l,r;
60 read(l); read(r);
61 int ans=1;
62 for(;;) {
63 int tp=qry(rt[r],1,sz,1,ans)-qry(rt[l-1],1,sz,1,ans);
64 if(tp<ans) break;
65 ans=tp+1;
66 }
67 printf("%d
",ans);
68 }
69 return 0;
70 }
说不出是什么心情,只能说.道不同不相为谋吧.
(UPD:似乎不知不觉间通往另一个end了)