HDU6851 Heart 倍增子集卷积

1 篇文章 0 订阅

分析

设多项式 A = ∑ i = 0 2 n − 1 a i x i , B = ∑ i = 0 2 n − 1 b i x i A=\sum\limits_{i=0}^{2^n-1}a_ix^i,B=\sum\limits_{i=0}^{2^n-1}b_ix^i A=i=02n1aixi,B=i=02n1bixi
C = A ∗ B = ∑ i = 0 2 n − 1 x i ∑ d ⊆ i a d b i − d C=A*B=\sum\limits_{i=0}^{2^n-1}x^i\sum\limits_{d \subseteq i}a_d b_{i-d} C=AB=i=02n1xidiadbid可以用 O ( n 2 2 n ) O(n^22^n) O(n22n)来完成
具体就是按照每个状态的最高位进行分组,然后卷n次,这样的复杂度计算是 O ( ∑ i = 1 n i 2 2 i ) = O ( n 2 2 n ) O(\sum\limits_{i=1}^{n}i^22^i) = O(n^22^n) O(i=1ni22i)=O(n22n)

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define MP make_pair
#define fi first
#define se second
#define PB push_back
#define CL clear
#define pii pair<int,int>
// #define int long long
using namespace std;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = (int)(1e6) + 10;
const int mod = 998244353;
const int inf = 1e9;
inline int rd() {
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
struct node{int p,b;}w[N];
bool cmp(const node &x,const node &y){return x.b<y.b;}
int a[22][(1<<21)+1],b[22][(1<<21)+1],c[(1<<21)+1];
void FMT(int *a,int n,int op) {
  for(int l=0;(1ll<<l)<n;++l)
    for(int i=0;i<n;++i)
      if(i&(1ll<<l)) a[i] = (1ll * a[i] + 1ll * op * a[i^(1ll<<l)] + mod) % mod;
}
int ans[(1<<21)+1];
signed main() {
  int n = rd();
  rep(i,1,n) w[i].p = rd(),w[i].b = rd();
  sort(w+1,w+n+1,cmp); int j = 1;
  while(w[j].b < 2 && j<=n) (a[__builtin_popcount(w[j].b)][w[j].b] += w[j].p) %= mod,j++; a[0][0] = 1; ans[1] = a[1][1]; 
  rep(i,2,21) {
    b[0][0] = 1; while(w[j].b < (1ll<<i) && j<=n) {
      (b[__builtin_popcount(w[j].b)][w[j].b] += w[j].p) %= mod;
      j++;
    }
    for(int k=0;k<=i;k++) FMT(a[k],(1ll<<i),1),FMT(b[k],(1ll<<i),1);
    for(int k1=i;k1>=0;k1--) {
      for(int k2=0;k2<=k1;k2++) for(int p=0;p<(1ll<<i);p++) {
        c[p] = (1ll * c[p] + 1ll * a[k2][p] * b[k1-k2][p]) % mod;
      }
      FMT(c,(1<<i),-1);
      memcpy(a[k1],c,sizeof(c));

      for(int p=0;p<(1ll<<i);p++) c[p] = 0;
    }
    for(int k=(1ll<<(i-1));k<(1ll<<i);k++) ans[k] = (ans[k] + a[__builtin_popcount(k)][k]) % mod;
    for(int k=0;k<=i;k++) for(int p=0;p<(1ll<<i);p++) b[k][p] = 0;
  }
  int q = rd(); while(q--){int x=rd(); printf("%d\n",ans[x]);}

  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值