• [模拟赛19-5-07]序列问题


    rctc:

    模拟赛T1map大暴力水过了...T3在GTX大佬一句“SPFA就好了呀”下乱口胡了一个SPFA里面套个DP竟然也A了...感觉RP用光了

    题目描述

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

    题解:

      看数据范围显然是DP无疑了,但是考试的时候感觉T3更可做,于是先做了T3,T2只打了暴力(其实想DP的时候还是推出过一个错误的转移式子的,幸好没肝T2)

      两个数相等就相当于两个数的 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]

      这是官方题解...上面的转移方程应该看得懂了...然后刷表法刷去就好了

      注意①:转移的时候因为and操作是无法“逆”过来的,所以我们把原数组倒过来存即可

      注意②:这里当前状态仅与另外一个状态有关,显然可以把i的那一维压掉

      恶心的是,这样只有60分...发现爆long long了

      然而尝试了unsigned long long ,__int128,unsigned __int128均是60分

      于是只好打起了高精度

      然后,神TM。。。TLE了,出题人有毒啊*1

      压位高精我按题解改的,题解压了9位,我想看看出题人到底有没有那么毒瘤,于是压了7位试试

      没错7位是过不了的...出题人有毒啊*2

      压了8位的代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define writeln(x)  write(x),puts("")
     5 #define writep(x)   write(x),putchar(' ')
     6 using namespace std;
     7 inline int read(){
     8     int ans=0,f=1;char chr=getchar();
     9     while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();}
    10     while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}
    11     return ans*f;
    12 }void write(int x){
    13     if(x<0) putchar('-'),x=-x;
    14     if(x>9) write(x/10);
    15     putchar(x%10+'0');
    16 }const int M=1e8;
    17 struct P{
    18     int len,a[40];
    19     P():len(1){memset(a,0,sizeof a);}
    20     void operator +=(const P &x){
    21         if(x.len>len) len=x.len;
    22         for(int i=1;i<=len;++i){
    23             a[i]+=x.a[i];
    24             if(a[i]>=M) a[i]-=M,++a[i+1];
    25         }if(a[len+1]) ++len;
    26         return;
    27     } 
    28     void Print(){printf("%d",a[len]);
    29         for(int i=len-1;i>=1;i--)printf("%08d",a[i]);
    30     }
    31 }f[2][2048][3];
    32 int a[1005],n;
    33 signed main(){
    34     freopen("sequence.in","r",stdin);
    35     freopen("sequence.out","w",stdout);
    36     n=read();
    37     for(int i=n;i>=1;--i) a[i]=read();
    38     f[0][1023][0].a[1]=1;
    39     for(int i=0;i<n;i++){
    40         int npw=i&1^1;
    41         for(int j=0;j<1024;++j)
    42             for(int k=0;k<=2;k++)
    43                 f[npw][j][k]=f[!npw][j][k];
    44         for(int j=0;j<1024;j++){
    45             int x=a[i+1]&j,y=a[i+1]^j;
    46             f[npw][x][1]+=f[!npw][j][0],f[npw][x][1]+=f[!npw][j][1];
    47             f[npw][y][2]+=f[!npw][j][1],f[npw][y][2]+=f[!npw][j][2];
    48         }
    49     }f[n&1][0][2].Print();
    50     return 0;
    51 }
  • 相关阅读:
    动手动脑及作业
    技能——沟通
    大道至简第三章读后感
    编写一个程序,用户输入两个数,求其加减乘除,并用消息框显示计算结果。
    动手动脑及课后实验
    大道至简第六章
    继承与接口
    产生随机数并窗口显示他们的和
    大道至简——失败也是积累
    动手动脑
  • 原文地址:https://www.cnblogs.com/zhenglw/p/10841185.html
Copyright © 2020-2023  润新知