暴力 或者 动态规划
TZL比赛时写的是动态规划的,太慢了,但是也可以过。
我自己赛后用暴力过了,当时想复杂了。
分析:
因为或运算的结果是只增不减的,所以当每一次或运算的结果大于等于m的时候,就可以直接break了。不剪枝的话会TLE超时。
但是仔细想一想这题的时间复杂度,用暴搜的话是O(n^2)。十万的数据里,五万乘以五万等于25亿,25秒肯定超时。所以说:这题数据是有多水,无下限啊!坑哥一下午!
我自己的暴力AC代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAXN 100005
int a[MAXN];
int main() {
int t, tt = 0;
scanf("%d", &t);
while(t--) {
int cnt = 0;
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
int tmp;
for(int i = 0; i < n; i++) {
tmp = a[i];
for(int j = i; j < n; j++) {
tmp = tmp|a[j];
if(tmp < m)
cnt++;
else //如果没有这个判断,会超时
break;
}
}
printf("Case #%d: %d\n", ++tt, cnt);
}
return 0;
}
TZL的动态规划AC代码:
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <map>
#include <set>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
using namespace std;
#define MAXN 101000
#define LOGMAXN 20
int dp[MAXN][LOGMAXN];
int logn, n, m;
int
calc_or(int i, int len)
{
int loglen = 0;
while ((1 << loglen) <= len) {
++loglen;
}
--loglen;
return dp[i][loglen] | dp[i + len - (1 << loglen)][loglen];
}
int
main(int argc, char *argv[])
{
int T, ans;
int num[MAXN];
scanf("%d", &T);
for (int k = 1; k <= T; ++k) {
scanf("%d %d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%d", num + i);
logn = 0;
while ((1 << logn) < n) {
++logn;
}
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; ++i) dp[i][0] = num[i];
for (int j = 1; j <= logn; ++j) {
for (int i = 0; i < n; ++i) {
if (i + (1 << (j - 1)) >= n) {
dp[i][j] = dp[i][j - 1];
} else {
dp[i][j] = dp[i][j - 1] | dp[i + (1 << (j - 1))][j - 1];
}
}
}
ans = 0;
int len, addlen, sum_or;
for (int i = 0; i < n; ++i) {
addlen = logn;
len = 0;
while (addlen >= 0) {
if (i + len + (1 << addlen) - 1 >= n) {
--addlen;
continue;
}
sum_or = calc_or(i, len + (1 << addlen));
if (sum_or < m) {
len += (1 << addlen);
}
--addlen;
}
ans += len;
}
printf("Case #%d: %d\n", k, ans);
}
return 0;
}
另外,HDU4737的Discuss里有一个O(30*n)的代码,挺犀利的。分享一下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;
const int MAX=100000+10;
int s[MAX],digit[33],pow[31]={1};
void digitAdd(int n){
int size=-1;
while(n)digit[++size]+=n%2,n=n/2;
}
void digitSub(int n){
int size=-1;
while(n)digit[++size]-=n%2,n=n/2;
}
int f(){
int size=0,sum=0;
for(int i=0;i<31;++i)sum+=(digit[i]>0)*pow[i];
return sum;
}
int main(){
for(int i=1;i<31;++i)pow[i]=pow[i-1]*2;
int t,n,m,Case=0,i,j,k,sum,ans;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(i=0;i<n;++i)scanf("%d",&s[i]);
sum=ans=i=j=k=0;
memset(digit,0,sizeof digit);
while(j<n){
if((f()|s[j])<m)digitAdd(s[j]),sum+=j-i+1,++j;
else {if(i<j)digitSub(s[i]);++i;}
if(i>j)j=i;
}
printf("Case #%d: %d\n",++Case,sum);
}
return 0;
}