您的位置:首页 > 其它

HDU 4737 A Bit Fun 解题报告

2013-09-15 10:38 399 查看
题目

题意:

有n个数,求有多少个数对(i, j) 使得 ai|ai+1|ai+2|
... | aj的值小于m。

题解:

这题有好多种解法:

解法1:

边读边做,将数拆开成二进制,用pre数组表示在这一位上最近出现1的位置。当做到第j个数的时候,固定数对的右端点为j,那么就在左边找左端点i。

从高位到低位和m比较,如果这一位上m是1,那么如果i~j间都是0,则肯定比m小,任取哪个做左端点都可以;如果i~j间有1,则这一位和m相等,看下一位。这一位上m是同理。

解法2:

枚举右端点,二分最远能满足要求的左端点。这里可以用RMQ的倍增法,预处理一个个区间的或值,那么查询就是log n的。

解法3:

还是先拆开数,记当前区间每一位上1的个数。如果右端点为j时,最远左端点为i,或值为x,那么右端点为j+1时,或值为x=x|aj+1。如果x不小于m,则右移i,同时减掉对应位置的1.当这一位1的个数为0时,x就剪掉对应值,直到x小于m为止。

解法1和解法3复杂度都是n*log n的,解法2是n*log n*log n,但是比解法1和解法2小个常数32,所以都是差不多的。

比赛时我用的解法1,后来才想到其它的。

//Time:609ms
//Memory:16416KB
//Length:1131B
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define MAXN 100010
char str[MAXN];
int arr[MAXN][40],ma[40];
int pre[MAXN],num[MAXN];
void cha(int n,int num[],int h)
{
for(int i=0;i<32;++i)   num[i]=n&1,n>>=1;
for(int i=0;i<32;++i)
if(num[i])  pre[i]=h;
}
int cal(int l,int r)
{
int ret=0;
for(int i=31;i>=0&&l<=r;--i)
if(ma[i]==1)
{
ret+=max(0,r-max(pre[i],l-1));
r=min(r,pre[i]);
}
else    l=max(l,pre[i]+1);
return ret+max(r-l+1,0);
}
int main()
{
//freopen("/home/moor/Code/output","r",stdin);
int ncase,n,m;
long long ans=0;
scanf("%d",&ncase);
for(int hh=1;hh<=ncase;++hh)
{
printf("Case #%d: ",hh);
scanf("%d%d",&n,&m);
cha(m-1,ma,0);
memset(pre,-1,sizeof(pre));
ans=0;
for(int i=0;i<n;++i)
{
int tmp;
scanf("%d",&tmp);
cha(tmp,arr[i],i);
ans+=cal(0,i);
}
cout<<ans<<'\n';
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: