序列问题

来源:互联网 发布:中医 数据库 编辑:程序博客网 时间:2024/06/06 00:12

题目描述
小H是个善于思考的学生,她正在思考一个有关序列的问题。

她的面前浮现出了一个长度为n的序列{ai},她想找出两个非空的集合S、T。

这两个集合要满足以下的条件:

  1. 两个集合中的元素都为整数,且都在 [1, n] 里,即Si,Ti ∈ [1, n]。

  2. 对于集合S中任意一个元素x,集合T中任意一个元素y,满足x < y。

  3. 对于大小分别为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),你能帮助她吗?

输入
第一行,一个整数n

   第二行,n个整数,代表ai。

输出
仅一行,表示最后的答案。

样例输入
4
1 2 3 3
样例输出
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

【数据范围】

30%: 1 <= n <= 1060%: 1 <= n <= 100100%: 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 的数组滚动即可。

代码(自己打的压位高精,比标程慢太多)

#include<bits/stdc++.h>#define ll long long#define W 1000000000using namespace std;inline int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}int n,m,p[1005];struct node{    int len,a[40];    inline void print()    {        printf("%d",a[len]);        for (int i=len-1;i;i--)            printf("%09d",a[i]);        return;    }}f[2][1025][3];node operator+(node a,node b){    int c[100],z=0,i;    a.len=max(1,a.len);    b.len=max(1,b.len);    for (i=1;i<=a.len||i<=b.len;i++)    {        c[i]=a.a[i]+b.a[i]+z;        z=c[i]/W;        c[i]%=W;    }    int l=max(a.len,b.len);    i=l+1;    while (z)    {        c[i]=z;        z=c[i]/W;        c[i]%=W;        i++;    }    a.len=i-1;    for (i=1;i<=a.len;i++)        a.a[i]=c[i];    return a;}int main(){    n=read();m=1024;    for (int i=n;i;i--) p[i]=read();    f[0][1023][0].a[1]=1;    int pre=0,suc=1;    for (int i=1;i<=n;i++)    {        suc=pre^1;        for (int j=0;j<m;j++)            for (int k=0;k<=2;k++)                f[suc][j][k]=f[pre][j][k];        for (int j=0;j<m;j++)        {            int a=p[i]&j,x=p[i]^j;            f[suc][a][1]=f[suc][a][1]+f[pre][j][0];             f[suc][a][1]=f[suc][a][1]+f[pre][j][1];             f[suc][x][2]=f[suc][x][2]+f[pre][j][1];             f[suc][x][2]=f[suc][x][2]+f[pre][j][2];        }        pre=suc;    }    f[pre][0][2].print();    return 0;}

标算

#include<bits/stdc++.h>#define ll long longusing namespace std;const int W=1e9;inline int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}int n,m,p[1005];struct node{    int len,a[40];    node():len(1){memset(a,0,sizeof(a));}    inline void operator+=(node &b)    {        b.len>len?len=b.len:0;        for (int i=1;i<=len;i++) {            a[i]+=b.a[i];            if (a[i]>=W) a[i+1]++,a[i]-=W;        }        if (a[len+1])len++;        return ;    }    inline void print()    {        printf("%d",a[len]);        for (int i=len-1;i;i--)            printf("%09d",a[i]);        return;    }}f[2][1025][3];int main(){    n=read();m=1024;    for (int i=n;i;i--) p[i]=read();    f[0][1023][0].a[1]=1;    int pre=0,suc=1;    for (int i=0;i<n;i++)    {        suc=pre^1;        for (int j=0;j<m;j++)            for (int k=0;k<=2;k++)                f[suc][j][k]=f[pre][j][k];        for (int j=0;j<m;j++)        {            int a=p[i+1]&j,x=p[i+1]^j;            f[suc][a][1]+=f[pre][j][0]; f[suc][a][1]+=f[pre][j][1];             f[suc][x][2]+=f[pre][j][1]; f[suc][x][2]+=f[pre][j][2];        }        pre=suc;    }    f[pre][0][2].print();    return 0;}
原创粉丝点击