zoukankan      html  css  js  c++  java
  • [题解] NOI Online #3 优秀子序列

    Luogu P6570

    (n) 为值域。

    首先有个 (mathcal O(3^{log_2n}) = mathcal O(n ^ {1.5})) 的子集 DP。

    式子很像子集卷积,但如果把每个数依次卷起来复杂度过高,不可接受。

    考虑分组,发现有相同一个二进制位的数必然不会被卷到一起。

    但如果按照一般二进制位分成 (log n) 组,再依次卷积,复杂度是 (mathcal O(n log^3 n)),其中每次卷积是 (mathcal O(n log^2 n))

    考虑限制每组元素的个数,按照最高位分组,这样每组的二进制长度受到限制 ,复杂度就会为:

    [sum_{i=1}^{log n} 2^i cdot i^2 = mathcal O(n log^2 n) ]

    其中 (i) 为每组的长度。

    Code

    当然可以继续压常数

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    #define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
    typedef long long ll;
    namespace io {
      const int SIZE = (1 << 21) + 1;
      char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
      #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
      char getc () {return gc();}
      inline void flush () {fwrite (obuf, 1, oS - obuf, stdout); oS = obuf;}
      inline void putc (char x) {*oS ++ = x; if (oS == oT) flush ();}
      template <class I> inline void gi (I &x) {for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;}
      template <class I> inline void print (I x) {if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;while (x) qu[++ qr] = x % 10 + '0',  x /= 10;while (qr) putc (qu[qr --]);}
      struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
    }
    using io :: gi; using io :: putc; using io :: print; using io :: getc;
    template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
    template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
    
    const int N = 262144;
    const int mod = 1000000007;
    int phi[N + 1], p[N + 1], pc = 0;
    bool np[N + 1];
    
    inline int add(int x, int y) {return x+y>=mod ? x+y-mod : x+y;}
    inline int sub(int x, int y) {return x-y<0 ? x-y+mod : x-y;}
    inline int mul(int x, int y) {return 1LL * x * y % mod;}
    inline void inc(int &x, int y=1) {x += y; if(x >= mod) x -= mod;}
    inline void dec(int &x, int y=1) {x -= y; if(x < 0) x += mod;}
    inline int power(int x, int y){
      int res = 1;
      for(; y; y>>=1, x = mul(x, x)) if(y & 1) res = mul(res, x);
      return res;
    }
    inline int inv(int x){return power(x, mod - 2);}
    
    void getPhi(int n){
      phi[1] = 1;
      for(int i=2; i<=n; i++){
        if(!np[i]) p[++pc] = i, phi[i] = i - 1;
        for(int j=1; j<=pc && i * p[j] <= n; j++){
          np[i * p[j]] = true;
          if(i % p[j] == 0){
            phi[i * p[j]] = phi[i] * p[j];
            break;
          }
          phi[i * p[j]] = phi[i] * (p[j] - 1);
        }
      }
    }
    
    void FWT(int a[], int len){
      for(int w=2, k=1; w<=len; w<<=1, k<<=1)
        for(int i=0; i!=len; i+=w)
          for(int j=i, li=i+k; j!=li; ++j)
            inc(a[j + k], a[j]);
    }
    void IFWT(int a[], int len){
      for(int w=2, k=1; w<=len; w<<=1, k<<=1)
        for(int i=0; i!=len; i+=w)
          for(int j=i, li=i+k; j!=li; ++j)
            dec(a[j + k], a[j]);
    }
    void subsetConv(int a[], int b[], int c[], int len){
      static int popc[N];
      static int A[19][N], B[19][N], C[19][N];
      for(int i=0; i<len; i++){
        popc[i] = popc[i >> 1] + (i & 1);
        A[popc[i]][i] = a[i];
        B[popc[i]][i] = b[i];
      }
      int n = popc[len - 1];
      for(int i=0; i<n; i++){
        FWT(A[i], len);
        FWT(B[i], len);
      }
      for(int k=0; k<=n; k++)
        for(int i=0; i<=k; i++)
          for(int p=0; p<len; p++)
            inc(C[k][p], mul(A[i][p], B[k - i][p]));
      for(int i=0; i<=n; i++) IFWT(C[i], len);
      for(int i=0; i<len; i++) c[i] = C[popc[i]][i];
      for(int i=0; i<=n; i++){
        fill_n(A[i], len, 0);
        fill_n(B[i], len, 0);
        fill_n(C[i], len, 0);
      }
    }
    
    int cnt[N];
    int a[19][N], num[N];
    
    int main(){
      getPhi(262144);
      int n;
      gi(n);
    
      static int high[N];
      for(int i=1; i<262144; i++)
        high[i] = high[i >> 1] + 1;
      int bit = 0;
      for(int i=0; i<n; i++){
        int x; gi(x);
        ++a[high[x]][x];
        upmax(bit, high[x]);
      }
      static int s[N];
      s[0] = power(2, a[0][0]);
      s[1] = mul(s[0], a[1][1]);
      for(int i=2; i<=bit; i++){
        a[i][0] = 1;
        subsetConv(s, a[i], s, 1 << i);
      }
    
      int sum = 0;
      for(int i=0; i<(1 << bit); i++)
        inc(sum, mul(phi[i + 1], s[i]));
      print(sum); putc('
    ');
      return 0;
    }
    
  • 相关阅读:
    部署K2 Blackpearl流程时出错(与基础事务管理器的通信失败或Communication with the underlying transaction manager has failed.)
    用SQL命令将查询结果集导出为文本文件
    SQL函数计算两个日期间的工作日天数
    CommonJS模块加载方法
    async 属性
    如何用VS.NET2005调式.asp和.aspx混合的web系统
    对web.config文件的节点进行加解密
    [转]看似简单的问题 静态方法和实例化方法的区别
    单引号(')和双引号(")
    从一个时间段中查找出星期为“Sunday”的日期
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-luogu6570.html
Copyright © 2011-2022 走看看