暴力 或者 动态规划
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; }