【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),你能帮助她吗?


思路:

如果满足a[s1] xor a[s2] xor a[s3] ... xor a[sp] = a[t1] and a[t2] and a[t3] ... and a[tq]a[s1]\ xor\ a[s2]\ xor\ a[s3]\ ...\ xor\ a[sp]\ =\ a[t1]\ and\ a[t2]\ and\ a[t3]\ ...\ and\ a[tq]
,那么就等价于前者的异或值与后者的与值异或起来等于0。
所以设f[i][j][3]f[i][j][3]表示ini\sim n的数字,异或起来为jj,已经到了 一个都没选 // 与区间 // 异或区间的方案数。
直接转移即可。
注意要压位高精。
时间复杂度O(n×Smax×maxn)O(n\times Smax\times maxn)。其中Smax=1024,maxnSmax=1024,maxn是高精度的位数。


代码:

#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;
}
posted @ 2019-11-11 20:00  全OI最菜  阅读(159)  评论(0编辑  收藏  举报