Codeforces Round #150 (Div. 1) A. The Brand New Function

来源:互联网 发布:怎么申请淘宝 编辑:程序博客网 时间:2024/05/18 01:19

题意

    给你 n(1<=n<=10^5) 个数,每个数的范围是 [0, 10^6] ,定义一个函数 f(L, R) :把从第 L 个数取到第 R 个数全部取“或”得到的数,考虑所有的 L 和 R,问你能够得到多少个不同的 f(L, R)

做法分析

    首先,没想法,但是我们知道这个结果一定不会超过 2^20,那么就先暴力试试吧:
        1、将所有数存在数组 a 中
        2、用 f[i][j] 表示从第 i 个到第 j 个,所有数“或”的值。其实我们可以在暴力枚举 j 的过程中再来暴力枚举 i ,这样就可以把二维数组化为一位数组了
        3、定义一个数组 flag,flag[i] 表示 值为 i 的数是否是 f(L, R) 的一个结果
        4、扫描 flag 数组,看哪些的值是 1,是就把答案加 1
        5、输出答案
                
for(int i=1; i<=n; i++)for(int j=1; j<i; j++){f[j]=f[j]|i;flag[f[j]]=1;}

    很不幸,在第 13 组数据 T 了。。。
    那该怎么优化,或者想其他的方法?
    这里是对上面的优化
    在上面的六行代码中,考虑到这样一个性质:第 2 行枚举 j 的时候其实是可以从 i-1 到 1 逆向枚举的,然后我们注意到 f[j] 表示的是什么:第 j 个元素到第 i 个元素之间所有元素“或”的值,为了方便,我们先把它用二维数组表示:f[j][i] , j<i 。仔细观察这样的一个过程:
    我们已经知道了 f[j, i-1] ,现在新添加一个 a[i] 进来并且更新所有的 f[j, i]=f[j, i-1] | a[i] ,这就是上面暴力过程做的事情
    注意这样一个事实:f[j, i]=f[j, k] | f[k+1, i] , 注意了,如果现在已经有了f[k+1, i]==f[k+1, i-1] ,那么对于所有的 1<=j<=k ,有 f[j, i] ==f[j, i-1]  ,如果再把 f 数组化成一维数组,那么我们的所有 f[j] , 1<=j<=k 就不用改变,也就是说,在逆向循环 j 的时候,一旦我们遇到了 f[j]==f[j] | a[i] ,我们就可以 break 掉了,这有点单调队列的感觉,但不是很强烈,不过相信时间复杂度应该不是 n^2 的了,然后试着交了一发,78msAC
        如果您还有其他的方法,欢迎回复给我,感激不尽!

AC传送通道

Codeforces Round #150 (Div. 1) A. The Brand New Function

代码

#include <iostream>#include <cstdio>using namespace std;int a, f[100005], fg[1<<20], n, cnt=0;int main(){    scanf("%d", &n);    for(int i=1; i<=n; i++)    {        scanf("%d", &a);        for(int j=i-1; j>=1; j--)            if(f[j]!=(f[j]|a)) fg[f[j]=f[j]|a]=1;            else break;        fg[f[i]=a]=1;    }    for(int i=0; i<(1<<20); i++)        if(fg[i]) cnt++;    printf("%d\n", cnt);}


原创粉丝点击