PIPI的炼金术

题目描述

PIPI身为一名化学砖家,通过他不懈的努力,终于研究出了炼金术:2Al+2Cu=2Au+Cl2↑
已知PIPI共有n件材料,每件材料的属性由只含有小写字母的字符串表示。PIPI可以将任意数量(但数量必须大于0)的材料放在一起炼制,只要这些材料每个字符的出现次数为偶数,即可炼金成功。比如PIPI共有2件材料,材料1的属性为:aab,材料2的属性为b。如果材料1和材料2放在一起炼制,a字符一共出现两次,b字符一共出现两次,每个字符出现次数均为偶数,因此可以炼金成功。
PIPI想知道,他有多少种炼金成功的方案?

输入

第一行输入一个正整数n,n<=35。
接下来n行,每行一个字符串,表示材料的属性,0<字符串长度<=10^5。

输出

输出PIPI炼金成功的方案数。

样例输入

1
2
3
2
aab
b

样例输出

1
1

对于每一个材料,用 26 位表示 26 个字母的奇偶性(只有奇偶影响结果),然后枚举所有的子集,判断是否可行,累加答案就可。

但是 n 很大,然后二分,但是子集可能覆盖两部分,对于第一部分,记录所有的状态,遍历第二部分的时候如果状态有与第一部分的状态相同,则答案累加(因为异或运算,相同值异或为0 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const int MAX=100000+10;
int n,ans,c[36][50],d[36];
char a[36][MAX];
map<int,int> num;
inline void calc(int y){
int res = 0;
fi(i,0,n/2)
if(y & (1<<i))
res ^= d[i];
num[res]++;
ans += res ? 0 :1;
}
inline void calc2(int y){
int res = 0;
fi(i,0,n-n/2)
if(y & (1<<i))
res ^= d[i + n/2];
ans += num[res];
ans += res ? 0 :1;
}
int main(){
freopen("in","r",stdin);
sf(n);
fi(i,0,n) {
scanf("%s",a[i]);
int l=strlen(a[i]);
fi(j,0,l) c[i][a[i][j]-'a']++;
fi(j,0,26)
if(c[i][j] & 1)
d[i] |= (1<<(j));
}
int x = pow(2,n/2)-1,y=x;
do{
if(y) calc(y);
y = (y-1) & x;
}while(y!=x);

x=pow(2,n-n/2)-1,y=x;
do{
if(y) calc2(y);
y = (y-1) & x;
}while(y!=x);

pf(ans);
return 0;
}