Description
小H是个善于思考的学生,她正在思考一个有关序列的问题。
她的面前浮现出了一个长度为n的序列{ai},她想找出两个非空的集合S、T。
这两个集合要满足以下的条件:
两个集合中的元素都为整数,且都在 [1, n] 里,即Si,Ti ∈ [1, n]。
对于集合S中任意一个元素x,集合T中任意一个元素y,满足x < y。
对于大小分别为p, q的集合S与T,满足 a[s1] xor a[s2] xor a[s3] … xor a[sp] = a[t1] and a[t2] and a[t3] … and a[tq].
小H想知道一共有多少对这样的集合(S,T),你能帮助她吗?
Input
第一行,一个整数n
第二行,n个整数,代表ai。
Output
仅一行,表示最后的答案。
Sample Input
4
1 2 3 3
Sample Output
4
【样例解释】
S = {1,2}, T = {3}, 1 ^ 2 = 3 = 3 (^为异或)
S = {1,2}, T = {4}, 1 ^ 2 = 3 = 3
S = {1,2}, T = {3,4} 1 ^ 2 = 3 & 3 = 3 (&为与运算)
S = {3}, T = {4} 3 = 3 = 3
Data Constraint
30%: 1 <= n <= 10
60%: 1 <= n <= 100
100%: 1 <= n <= 1000, 0 <= ai < 1024
分析:
30%:枚举每个数所在的集合或者不选,然后判定即可。复杂度 O(n*3^n)。
60%: Dp,两个数相等就相当于两个数的 xor 为 0。设 f[i][j][k=0..2]代表 处理到第 I 个数,
如果 k = 1 代表 and 值为 j,如果 k = 2 代表 xor 值为 j,如果 k = 0 则代表一个元素都没
取。所以很容易得到方程:
f[i][j][0] = f[i + 1][j][0]
f[i][j & ai][1] = f[i + 1][j][1] + f[i + 1][j][0] + f[i + 1][j & ai][1]
f[i][j ^ ai][2] = f[i + 1][j][1] + f[i + 1][j][2] + f[i + 1][j ^ ai][2];
最后 f[1][0][2]就是答案, 复杂度为 O(n * 1024 * 3)
DP 还可以分开用 f[i][j]和 g[i][j]表示前 i 个 xor 值为 j,后 i 个 and 值为 j 的方案数,
随后枚举分界点 k 来求总方案数。复杂度 O(n * 1024 * 3)。
100%:满分数据需要高精,答案位数较大,需要进行压位来防止 TLE,因为不知道答案的
位数究竟多大,压位后高精数组仍需要开的较大一些,所以原 DP 的数组滚动即可。
代码:
const
mo=100000000;
type
arr=array[0..40] of longint;
var
n,i,j,p,q:longint;
st:string;
a:array[1..1000] of longint;
f:array[0..1,0..1023,1..2,0..40] of longint;
function max(x,y:longint):longint;
begin
if x>y then exit(x) else exit(y);
end;
procedure plus(var x:arr;y:arr);
var
i:longint;
begin
for i:=1 to max(x[0],y[0]) do begin
x[i]:=x[i]+y[i];
x[i+1]:=x[i+1]+x[i] div mo;
x[i]:=x[i] mod mo;
end;
x[0]:=max(x[0],y[0]);
if x[x[0]+1]>0 then inc(x[0]);
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
for i:=n downto 1 do begin
p:=i mod 2; q:=(i+1) mod 2;
f[p][a[i]][1][0]:=1;
f[p][a[i]][1][1]:=1;
for j:=0 to 1023 do begin
plus(f[p][j][1],f[q][j][1]);
plus(f[p][j and a[i]][1],f[q][j][1]);
plus(f[p][j][2],f[q][j][2]);
plus(f[p][j xor a[i]][2],f[q][j][2]);
plus(f[p][j xor a[i]][2],f[q][j][1]);
end;
fillchar(f[q],sizeof(f[q]),0);
end;
for i:=f[1][0][2][0] downto 1 do begin
str(f[1][0][2][i],st);
if i<>f[1][0][2][0] then for j:=1 to 8-length(st) do st:='0'+st;
write(st);
end;
if f[1][0][2][0]=0 then writeln(0);
end.