题目
给你长度为N的序列,序列里的数只包含0到20。表示第i个位置的颜色。
要求计算出满足要求的子序列总数
子序列的含有的每个颜色数的总和都是偶数。
题解思路
比赛的时候想着什么暴力双指针一条路走到黑都没写出来。
这里颜色数很少,所以我们可以直接用21位的二进制数表示出来。
即0表示含偶数或者0个,1表示奇数个。
这样我们就能表示1到n的所有状态了。
用异或运算传递一下之前状态的结果。
再判断这个状态的是否含奇数个这个颜色。
如果是就变成这个位数就0,减去对应的幂次。
不是就加上对应的幂次。
这样我们就好像得出了每个位置的每种颜色的前缀和。
答案的获取。
我们要获取的是状态里只有0的数,这样答案只有两种来源。
之前的和这个状态相等的数,他们相减就可以得出这个区间为0。
还有一种是本身为0的情况。这种是从起点开始延申到这个位置的。直接加起来即可。
思路就是利用二进制来表示每个位置的每种颜色的状态。
挺难想到的。
以后看到种类少的优先进行二进制表示状态。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <unordered_map>
using namespace std;
const int INF = 0x3f3f3f3f;
int f[1000100] ;
int a[1000100] ;
int mi[30] ;
int main ()
{
ios::sync_with_stdio(false);cin.tie(0);
unordered_map < int , int > mp ;
mi[1] = 1 ;
long long ans = 0 ;
for ( int i = 2 ; i <= 21 ; i++ )
mi[i] = mi[i-1]*2 ;
int n ;
cin >> n ;
f[0] = 0 ;
for ( int i = 1 ; i <= n ; i++ )
{
int t1 ;
cin >> t1 ;
a[i] = t1 ;
f[i] ^= f[i-1] ;
if ( f[i] >> (t1) & 1 )
f[i] -= mi[t1+1] ;
else
f[i] += mi[t1+1] ;
ans += mp[f[i]] ;
mp[f[i]]++ ;
// cout << f[i] << "\n" ;
}
cout << ans + mp[0] << "\n" ;
return 0 ;
}