15nod 1674 区间的价值 V2 ( 好题 )

链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1674

两个多小时写出来这个题好开心撒。

思路:我们对于位置i 的数,考虑以这个数结尾的区间的价值。  考虑他的每一个二进制位,如果第j 位 的二进制是0  那么这一位就不用管了,  因为与起来肯定是0 的,所以这一位去乘以 别的位肯定也是0  所以我们只考虑 数a[i]  的二进制为 1 的位,如果该位为1,那么我只需要找到该位的第一个为0 的位置在哪里,因为在这个位置之前的肯定与起来肯定是0  所以只考虑 [in, i ]  [in+1,i]  [in+2,i ] ...... 这些区间,同时还要满足或的情况,那么肯定就是 考虑当前位 第一为1 的位置在哪里。  然后就做出来了。

还用了个输入挂。。 

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
//  外挂 1

template <class T>
bool scan_d(T &ret)
{
    char c;
    int sgn;
    T bit = 0.1;
    if (c=getchar(), c==EOF)
    {
        return 0;
    }
    while (c!='-'&& c!='.'&& (c<'0'||c>'9'))
    {
        c = getchar();
    }
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9')
    {
        ret = ret * 10 + (c - '0');
    }
    if (c == ' ' || c == '\n')
    {
        ret *= sgn;
        return 1;
    }
    while (c = getchar(), c >= '0' && c <= '9')
    {
        ret += (c - '0') * bit, bit /= 10;
    }
    ret *= sgn;
    return 1;
}

template <class T>
inline void print_d(T& x)
{
    if (x > 9)
    {
        print_d(x/10);
    }
    putchar(x % 10 + '0');
}


const int N =1e5+5;
const ll mod=1e9+7;

int a[N];
int flag[N][40];
int sum[N][40];
int pre1[N][40];
int pre2[N][40];
int n;

ll yi[40];

void init_yi()
{
    yi[0]=1;
    for(int i=1;i<=65;i++) yi[i]=yi[i-1]*2%mod;
}

void zhuan(int i)
{
    int x=a[i];
    int cnt=0;
    while(x)
    {
        sum[i][++cnt]=x%2;
        flag[i][cnt]=x%2;
        x/=2;
    }
}

void getpre()
{
    for(int i=1;i<=n;i++){
        for(int j=1;j<=35;j++){
            sum[i][j]=sum[i-1][j]+sum[i][j];
        }
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=35;j++)
        {
            if(flag[i][j])
            {
                int l,r,mid;
                int in=0;
                l=1; r=i;
                while(l<=r)
                {
                    mid=(l+r)>>1;
                    if(sum[i][j]-sum[mid-1][j]==i-mid+1)
                    {
                        in=mid;
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                pre1[i][j]=in;// 找到前边第一个 0  in-1  为第一个0 的位置
            }
        }
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=35;j++)
        {
            int l,r,mid;
            int in=0;
            l=1; r=i;
            while(l<=r)
            {
                mid=(l+r)>>1;
                if(sum[i][j]-sum[mid-1][j]!=0)
                {
                    in=mid;
                    l=mid+1;
                }
                else r=mid-1;
            }
            pre2[i][j]=in;// 找到前边第一个 1   in 为第一个1 的位置
        }
    }

}

int main()
{
    init_yi();
    scan_d(n);
    for(int i=1;i<=n;i++)
    {
        scan_d(a[i]);
        zhuan(i);
    }

    getpre();
    ll ans=0;

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=35;j++)
        {
            if(flag[i][j]==1)
            {
                int l,r,mid;
                int in=pre1[i][j];
                ll cnt=i-in+1;
                for(int k=1;k<=35;k++)
                {
                    if(sum[i][k]-sum[in-1][k]!=0)
                    {
                        int in1=pre2[i][k];
                        ll tmp=in1-in+1;
                        ll num=yi[j-1]*yi[k-1]%mod;
                        ans=(ans+tmp*num%mod)%mod;
                    }
                }

               // printf("L  %d  R  %d\n",in,i);
               // printf("** i %d j %d ans %lld\n",i,j,ans);
            }
        }
    }

    printf("%lld\n",ans);

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值