【JZOJ3889】序列问题【dp】【高精度】
题目:
题目链接:https://jzoj.net/senior/#main/show/3889
小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),你能帮助她吗?
思路:
如果满足
,那么就等价于前者的异或值与后者的与值异或起来等于0。
所以设表示的数字,异或起来为,已经到了 一个都没选 与区间 异或区间的方案数。
直接转移即可。
注意要压位高精。
时间复杂度。其中是高精度的位数。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1001,Smax=1024,MAXN=40;
int n,a[N];
struct node
{
ll a[MAXN+1];
}f[2][Smax][3];
node operator +(node a,node b)
{
node c;
ll t=0;
for (register int k=MAXN;k>=1;k--)
{
c.a[k]=a.a[k]+b.a[k]+t;
t=c.a[k]/100000000;
c.a[k]%=100000000;
}
return c;
};
int main()
{
scanf("%d",&n);
for (register int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[(n+1)&1][Smax-1][0].a[MAXN]=1;
for (register int i=n;i>=1;i--)
{
int id=i&1;
memset(f[id],0,sizeof(f[id]));
for (register int j=0;j<Smax;j++)
{
f[id][j][0]=f[id^1][j][0];
f[id][j&a[i]][1]=f[id][j&a[i]][1]+f[id^1][j][1]+f[id^1][j][0];
f[id][j^a[i]][2]=f[id][j^a[i]][2]+f[id^1][j][2]+f[id^1][j][1];
f[id][j][1]=f[id][j][1]+f[id^1][j][1];
f[id][j][2]=f[id][j][2]+f[id^1][j][2];
}
}
int i=1;
while (!f[1][0][2].a[i] && i<=MAXN) i++;
if (i>MAXN) return !printf("0");
printf("%lld",f[1][0][2].a[i]);
for (i++;i<=MAXN;i++) printf("%08lld",f[1][0][2].a[i]);
return 0;
}