坑爹的题目。不过不能说不是一道挺好的题目。
坑主要坑在,妹的我一样的复杂度,写的姿势略差了点然后就一直超时。
比赛的时候我还直接就看错题目,把AND运算看成了OR。。。还敲完交了一发。
这题很容易想到:
因为给出的数字只有13位,所以每位用2位二进制表示。
如:
00 1的个数为偶数,最后的结果为0
01 1的个数为奇数,最后的结果为0
10 1的个数为偶数,最后的结果为1
11 1的个数为奇数,最后的结果为1
这样就可以转移求出最后的结果。然而这样做的复杂度是O(50*2^26)>1e9. 肯定不行。
仔细想想我们可以发现,当出现了0以后结果一定为0。这样用2位二进制位表示这个信息有些多余了。我们可以用三进制0,1,2和多余附加的一个标志位来表示这种信息。
如果某一位一直都是1则用0表示,如果出现了0,则用1,2分别代表出现奇数个1和偶数个1。 再配上附加的标志位来记录当前为止已加入数个数的奇偶性。分析下就可以发现,这样是能够把每一种情况表示出来的。
原先得用2^26=67108864种状态表示,现在只需要2*3^13=3188646. 直接就相差了20倍。wonderful!
然后剩下来还有一个关键问题,怎样将转移的O(13)->O(1)
PS:我原先的思想是,预处理,对于2*3^13的每种状态对应2^26某一种。结果一直TLE,这种做法会有比较大的内存消耗,而且预处理时间也较长。
可以预处理7位的所有情况,也就是把13位分成两半,因为对这两半的操作都是一样的,所以只需要预处理一半就行了,然后空间消耗变得非常小,转移操作变成O(1).
Rikka with Sequence
描述
众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习,其中有一道是这样的:
勇太有n个[0,8192)中的整数,现在六花可以从中选出若干个数(不可以不取),她的方案需要满足她选出的所有数的异或和恰好等于它们AND(二进制与运算)起来的值,现在勇太想让六花求出满足条件的方案数。
当然,这个问题对于萌萌哒六花来说实在是太难了,你可以帮帮她吗?
输入
第一行一个整数n,接下来一行里n个整数。
1<=n<=50
输出
输出一行表示答案。
- 样例输入
-
3 1 1 1
- 样例输出
-
4
// // main.cpp // hiho19 // // Created by 陈加寿 on 16/3/20. // Copyright © 2016年 chenhuan001. All rights reserved. // #include <iostream> #include <stdio.h> #include <string.h> #include <vector> #include <time.h> #include <math.h> #include <algorithm> using namespace std; #define K 8200 int g[55]; long long dp[2][1600000][2]; int chg[2200][130][2]; int chg1[2200][130][2]; void init() { int save[15]; memset(save,0,sizeof(save)); for(int i=0;i<2187;i++) { for(int j=0;j<(1<<7);j++) { for(int p=0;p<2;p++) { //然后你想怎么搞就怎么搞吧。 int tmp1=0,tmp2=0; for(int k=0;k<7;k++) { if(save[k] == 0) { tmp1 |= (1<<k); } } for(int k=0;k<7;k++) { if(save[k] == 0) { if(p) tmp2|=(1<<k); } else if(save[k] == 1) tmp2|=(1<<k); } tmp1 &= j; tmp2 ^= j; //然后再转变回去。 int tmp=1; int sum=0; for(int k=0;k<7;k++) { if( (tmp1&(1<<k))!= 0) { ; } else if( (tmp2&(1<<k))!= 0 ) { sum += tmp; } else if( (tmp2&(1<<k))== 0 ) { sum += 2*tmp; } tmp *=3; } chg[i][j][p] = sum; chg1[i][j][p] = (chg[i][j][p]/3)*2187; } } int tj=0; save[tj]++; while(save[tj]>=3) { save[tj+1]++; save[tj]=0; tj++; } } } //是与运算 //clock_t be,ed; //void checktime() //{ // ed=clock(); // cout<<(double)(ed-be)/CLOCKS_PER_SEC<<endl; // be=clock(); // //} int main() { //看错题目??? 不思考看题解??? 你是SB吗 int n; cin>>n; // 我真是日了狗了 for(int i=0;i<n;i++) { scanf("%d",g+i); //13 = 1594323(nn,13); //1594323 = 1594323(1594323,tcnt); } // if(13 == 0) // { // cout<<((1LL)<<n)-1<<endl; // return 0; // } //be=clock(); init(); //checktime(); int a=0; memset(dp,0,sizeof(dp)); dp[a][0][0] = 1; for(int i=0;i<n;i++) { memset(dp[a^1],0,sizeof(dp[a^1])); int up=g[i]>>6;//这个应该是移6位吧 int down = g[i]&((1<<7)-1); int j1=0,j2=0; for(int j=0;j<1594323;j++) { j2= j/729; j1=j%2187; for(int p=0;p<2;p++) { //dp[a^1][j] += dp[a][j]; int sum=0; if(dp[a][j][p] != 0) { dp[a^1][j][p] += dp[a][j][p]; sum = chg1[j2][up][p]+chg[j1][down][p]; dp[a^1][sum][p^1] += dp[a][j][p]; } } // j2++; // if(j2>=(2187)) // { // j2=0;j1++; // } } a = a^1; } //checktime(); long long ans=0; int save[15]; memset(save, 0, sizeof(save)); for(int j=0;j<1594323;j++) { for(int p=0;p<2;p++) { int flag=0; for(int k=0;k<13;k++) if(save[k]==1) { flag=1; break; } else if(save[k]==0 && p==0) { flag=1; break; } if(flag==0) ans += dp[a][j][p]; } int tj=0; save[tj]++; while(save[tj]>=3) { save[tj+1]++; save[tj]=0; tj++; } } //checktime(); cout<<ans<<endl; return 0; }