hdu5908 Abelian Period 暴力 小小小小的优化

来源:互联网 发布:js return void 编辑:程序博客网 时间:2024/05/22 11:23

题目链接


这道题是昨天晚上(10.1)的 bestcoder #88 的1002题,一开始交的时候(小数据)过了,最后还是崩了...

想想算法没问题啊,从1到n/2枚举可能的k,对于 未在之前的过程中被确认为可以的 并且 整除n的k 去判断是否可以。

判断的过程就是首先在第一段即0-k-1的区间中记录下各个数字出现的次数,然后对于之后的每一个i*k-i*k+k-1去判断是否和第一段相符,一旦出现矛盾就跳出来。

对于任意一个符合条件的k,它的整数倍(如果也整除n的话)都是符合条件的,这一点是很显然的,那么标记一下,省去后面的判断(就类似于素数筛法里面做的那样)。

好像也想不到什么其他的好方法了,能够改动的都是一些细节部分啊......可是就是TLE了。


TLE代码如下:

//1002.cpp#include <iostream>#include <climits>#include <cmath>#include <cstring>using namespace std;#define maxn 100010int a[maxn],tot[maxn],tott[maxn];bool exist[maxn];int main(){    int T,n,minn,maxx,i,j,k;    bool flag;    cin >> T;    while (T--)    {        cin >> n;        maxx = 0;        minn = INT_MAX;        for (i=0;i<n;++i)        {            cin >> a[i];            maxx = max(maxx,a[i]);            minn = min(minn,a[i]);        }        memset(exist,0,sizeof(exist));        for (i=1;i<=n/2;++i)        {            if (exist[i] || n%i!=0)                continue;            memset(tot,0,sizeof(tot));            memset(tott,0,sizeof(tott));            for (j=0;j<i;j++)                ++tot[a[j]];            for (j=1;j<n/i;++j)            {                flag = true;                for (k=minn;k<=maxx;++k)                    tott[k] = tot[k];                for (k=j*i;k<j*i+i;++k)                    --tott[a[k]];                for (k=minn;k<=maxx;++k)                    if (tott[k]!=0)                    {                        flag = false;                        break;                    }                if (!flag)                        break;            }            if (flag)            {                j = 1;                while (i*j<=n)                {                    if (n%(i*j)==0)                        exist[i*j] = true;                    ++j;                }            }        }        for (i=1;i<n;++i)            if (exist[i])                cout << i << " ";        cout << n << endl;    }    return 0;}


那么既然这样的话就稍微改动一下细节吧。

上面的写法花了很多时间在无用元素的处理上(对本身就不存在的元素去判断是不是0)和每次判断一个k是否可行的时候都要做的两个memset。

既然这样的话就在这里处理一下吧,加一个数组去记录需要对哪些进行判断,需要对哪些最后清零。

然后这样就过了。

没错就是改进了这个地方然后就过了。

其实还有其他地方可以小小改进,比如说判断的k的范围,对每个数字出现的次数进行统计,可能分成的段数肯定小于等于它们的gcd;在这个基础上直接用总的个数/分的段数得到每个数字出现的次数,也就没有必要去单独拿出第一段统计了(其实也差不多...)


AC代码如下:

//1002.cpp#include <iostream>#include <climits>#include <cmath>#include <cstring>using namespace std;#define maxn 100010int a[maxn],tot[maxn],tott[maxn],in[maxn];bool exist[maxn];int main(){    int T,n,minn,maxx,i,j,k,t,kk;    bool flag;    cin >> T;    while (T--)    {        cin >> n;        maxx = 0;        minn = INT_MAX;        for (i=0;i<n;++i)            cin >> a[i];        memset(exist,0,sizeof(exist));        memset(tot,0,sizeof(tot));        for (i=1;i<=n/2;++i)        {            if (exist[i] || n%i!=0)                continue;            t = 0;            for (j=0;j<i;++j)            {                ++tot[a[j]];                in[t++] = a[j];            }            for (j=1;j<n/i;++j)            {                flag = true;                for (k=0;k<t;++k)                    tott[in[k]] = tot[in[k]];                for (k=j*i;k<j*i+i;++k)                    --tott[a[k]];                for (k=0;k<t;++k)                    if (tott[in[k]]!=0)                    {                        for (kk=0;kk<t;++kk)                            tot[in[kk]] = 0;                        flag = false;                        break;                    }                if (!flag)                        break;            }            if (flag)            {                j = 1;                while (i*j<=n)                {                    if (n%(i*j)==0)                        exist[i*j] = true;                    ++j;                }            }        }        for (i=1;i<n;++i)            if (exist[i])                cout << i << " ";        cout << n << endl;    }    return 0;}


感觉有点小悲伤,就是因为这一点点的差别,直接导致了TLE,1002就没分了。

1001也是死在了细节上面,其中一步是

sum += (tot+1)*tot/2;

tot开的是int(因为tot的最大值是100000),sum开的是long long,以为可以类型转换过去的(蠢了...),然后就跪了。显然不可以啊,int*int再怎么也不会转化到long long去啊。

于是1001就在hack阶段给别人做出了贡献...明明是这么简单的题却就又没分了。

觉得这个错误纯粹是缺乏经验,再加上想当然了一下并且偷了个懒,只要把tot开成long long或者是写成 sum += (long long)(tot+1)*tot/2; 就可以了啊...

表示一下小心痛,以后会在这些地方多加注意的。

宝贵的经验教训啊。

0 0
原创粉丝点击