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

题意

    给你 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传送通道

代码

#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);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值