coci2014 contest#1 T3-PIRAMIDA—— 数学

来源:互联网 发布:怎样成为数据分析师 编辑:程序博客网 时间:2024/05/18 03:05

题目大意,将一串字符串按照Z型排列为N行的金字塔,给出K个询问,第i行有几个x字符。输出询问的每行中出现对应字符的个数。



样例输入:

6
JANJETINA
5
1 J
1 A
6 N
6 I
5 E

样例输出:

1

0

2

1

1

题目分析:

例如单词:ABCDEDFG
A            1
CB           2
DEF          3不够一个单词长度的时候 
CBAG         4 以下就是分两截或三截的时候 
DEFGA        5
GFEDCB       6
ABCDEFGA     7
CBAGFEDCB    8
DEFGABCDEFG  9

首先,让我们注意到,将单词方向从一行更改为一行不会对某行中的单个字母的数量产生任何影响。

一个简单的解决方案是模拟将字母写入每一行并计数,用矩阵p [26] [N]记录中单个字母的出现次数,然后从每个查询的矩阵中输出值。该解决方案的空间复杂度为O(N)和时间复杂度O(N * N + K),这足以达到总点数的50%。

为了获得更有效的解决方案,我们需要检查如何在每行中字符串的情况。有两个选择:

1)对于一些i和j,s = w [i..j]。换句话说,string是字符串w的子串。

2)对于一些i和j,s = w [i..L] + x * w + w [0..j]。换句话说,字符串

字符串后半截(可能是空的),然后是整个

字重复xtimes,最后,字符串w的后缀(可能是空的)

例如,如果这个词应该是“ABCD”,金字塔的第12行将是:

CDABCDABCDAB = CDA + 2 * ABCD + AB = w [1..4] + 2 * w + w [0..2]

为了确定某个行的x,i和j,我们需要能够确定该行中的单词开头的字母。很容易显示,对于行r,该位置等于分割数r *(r1)/ 2与m的剩余部分。有必要认真实施这个公式,因为rcan可能非常大。当我们计算出位置时,很容易确定参数x,i和j。

现在我们可以想出某些情况下的公式。令f(c,i,j)为

子串w [i..j]中字母的第C个字母的出现次数。该

公式如下:

1) 字母出现的次数= f(c,i,j)

2)字母出现的次数 = f(c,i,L)+ x * f(c,0,L)+ f(c,0,j)

为了有效地实现这个解决方案,我们需要能够快速计算函数f的值。我们可以通过构造矩阵p [26] [L]来完成,其中通过前缀和和p[i][j]上的元素告诉我们字母的第i个字母在子串w [0..j]中出现的次数。然后我们用公式计算f(c,i,j)。

该解决方案的内存复杂度为o(26*L),时间复杂度为oM + K)。

 

根据题目要求,由于最大字符串长度较大,10^6,所以该解决方案足够达到70%的总分,因此尺寸26 * L的矩阵不适合32 MB。

有必要单独解决每个字母的查询。在这种情况下,代替维度26 * L的矩阵,尺寸L的矩阵就足够了,那怎么省空间呢,那就不能存储每个每个字母了,只好存储一个字母,那针对每个字母预处理的,然后查询的话,时间复杂度就成了o(k*m),不行。我们发现,所有的查询中26个字母会重复出现,所以预处理一次,就将所有关于这个单词的查询都统计出来。26次预处理,分别计算关于这个字母的查询。这实际上查询的离线算法。

必修技能:预处理,数学

类别:ad-hoc,预处理

参考代码:

dhz50分的写法

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;long long cnt = 0, n, k, qusl, st, srh, el;char qusn, map[1000009];int main(){    cin>>n;    scanf("%s",&map);    int num = (n + n * n) / 2;    int len = strlen(map);    cin>>k;    for(int i = 0;i < k;i++){        cin>>qusl>>qusn;        st = (qusl * qusl - qusl) / 2;        srh = st % len;        el = num % len;        int j = srh;        for(int i = 0;i < qusl;i++){            if(map[j] == '\0') j = 0;            if(map[j] == qusn) cnt++;            j++;        }        cout<<cnt<<endl;        cnt = 0;    }} 

70分写法,超内存了

#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int MAXM = 1000000;const int MAXK = 50000;struct query {long long row;int id;query(long long row = 0, int id = 0):row(row), id(id) {}};int n, m, k;char w[MAXM+1];//字符串 int p[26][MAXM];//字母x在字符串中的前缀和 long long ans;//答案 //vector<query> Q[26];long long calc_pos(long long row) {long long a, b;if(row & 1) { a = row; b = (row+1)/2; } else {a = row/2;b = row+1;}a %= m;b %= m;return a * b % m;}int f(int p1, int p2,char c) {//[p1,p2]区间字母x有多少?p1=0怎么办?不能p[-1]吧。所以单独处理下 return p[c][p2] - (p1 != 0 ? p[c][p1-1] : 0);}long long solve(long long r,int c) {int p = calc_pos(r-1);//上一行剩下多少,单词的起始点 long long ans = 0;long long len = r-(m-p);//这一行从头开始的有多少 if(p+r <= m) return f(p, p+r-1,c);//不够一个单词长度的时候,在[p,p+r-1] ,比如下面第2,3行 else ans = f(p, m-1,c);//找出单词后半截; ans += (long long)f(0, m-1,c) * (len/m);//找出完整的 if(len%m) ans += f(0, len%m-1,c);//找出单词前半截 return ans;}int main( ) {scanf("%d", &n);scanf("%s", w);m = strlen(w);for(int i=0;i<m;i++){//先预处理出所有的前缀和 for(int j=0;j<26;j++)p[j][i]=p[j][i-1];int j=w[i]-'A';p[j][i]=1;if(i>0)p[j][i]+=p[j][i-1];}scanf("%d", &k);for(int i = 0; i < k; ++i) {//然后处理每个查询 char c; long long a;scanf("%lld %c", &a, &c);ans=solve(a,c-'A');printf("%lld\n", ans);}return 0;}

100分写法

#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int MAXM = 1000000;const int MAXK = 50000;struct query {long long row;int id;query(long long row = 0, int id = 0):row(row), id(id) {}};int n, m, k;char w[MAXM+1];int p[MAXM];long long ans[MAXK];vector<query> Q[26];long long calc_pos(long long row) {long long a, b;if(row & 1) { a = row; b = (row+1)/2; } else {a = row/2;b = row+1;}a %= m;b %= m;return a * b % m;}int f(int p1, int p2) { return p[p2] - (p1 != 0 ? p[p1-1] : 0);}long long solve(long long r) {int p = calc_pos(r-1);long long ans = 0;long long len = r-(m-p);if(p+r <= m) return f(p, p+r-1);else ans = f(p, m-1);ans += (long long)f(0, m-1) * (len/m);if(len%m) ans += f(0, len%m-1);return ans;}int main(void) {scanf("%d", &n);scanf("%s", w);m = strlen(w);scanf("%d", &k);for(int i = 0; i < k; ++i) {char c; long long a;scanf("%lld %c", &a, &c);Q[c-'A'].push_back(query(a, i));}for(int i = 0; i < 26; ++i) {for(int j = 0; j < m; ++j) {if(w[j]-'A' == i) p[j] = 1; else p[j] = 0;if(j > 0) p[j] += p[j-1];}for(int j = 0; j < Q[i].size(); ++j) ans[Q[i][j].id] = solve(Q[i][j].row);}for(int i = 0; i < k; ++i) printf("%lld\n", ans[i]);return 0;}



阅读全文
0 0
原创粉丝点击