[BITSET 分块] BZOJ5087. polycomp

本文介绍了一种使用bitset和块划分技巧来优化多项式运算的方法,将原始算法的时间复杂度从O(n^3/32)优化到了O(n^3/320),并通过示例代码展示了具体的实现细节。

陈老师神题×2

因为多项式的系数是0/1,那么可以用bitset压位优化

但是 O(n332)O(n332) 不够优

考虑每十位分一个块,令 gS=9i=0SibigS=∑i=09SibiSiSi 表示 SS 二进制中的第 i

预处理是 O(210n32)O(210n32) 然后每一块直接求就好了

复杂度就优化成 O(n3320)O(n3320)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <bitset>

using namespace std;

typedef bitset<8001> poly;

int n,m,t,high[1<<10|10];
poly a,b,md,d,ans,f[15],g[1<<10|10];

inline void add(poly a,poly b,poly &c){ c=a^b; }

inline void mul(poly a,poly b,poly &c){
  c=0;
  for(int i=0;i<=t;i++)
    if(a[i]) c^=b<<i;
  for(int i=t+t;i>=t;i--)
    if(c[i]) c^=md<<(i-t),c[i]=0;
}

inline void show(poly x){
  for(int i=0;i<=t;i++,cerr<<' ')
    cerr<<(x[i]?'1':'0');
  cerr<<endl;
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  scanf("%d",&n);
  for(int i=0,x;i<=n;i++)
    if(scanf("%d",&x),x) a.set(i);
  scanf("%d",&m);
  for(int i=0,x;i<=m;i++)
    if(scanf("%d",&x),x) b.set(i);
  scanf("%d",&t);
  for(int i=0,x;i<=t;i++)
    if(scanf("%d",&x),x) md.set(i);
  md[t]=0;
  for(int i=m;i>=t;i--)
    if(b[i]) b^=md<<(i-t),b[i]=0;
  f[0].set(0);
  for(int i=1;i<=10;i++) mul(f[i-1],b,f[i]);
  g[1].set(0);
  for(int S=2;S<(1<<10);S++){
    high[S]=high[S>>1]+1;
    g[S]=g[S-(1<<high[S])]^f[high[S]];
  }
  poly prod; prod.set(0);
  for(int i=0;i<=n;i+=10){
    int mask=0;
    for(int j=i;j<i+10;j++) mask|=a[j]<<(j-i);
    mul(g[mask],prod,d);
    ans^=d; mul(prod,f[10],prod);
  }
  while(!ans[t] && ~t) t--;
  if(!~t) puts("0 0");
  else{
    printf("%d ",t);
    for(int i=0;i<=t;i++) printf("%d%c",ans[i]?1:0,i==t?'\n':' ');
  }
  return 0;
}
这段 Java 代码定义了一个名为 `getTotalMemLen4Init` 的方法,用于计算给定内存初始化指令列表中所有内存地址的**总覆盖长度**,且**去重**了重复覆盖的地址。它使用了 `BitSet` 来高效地处理地址范围的重叠与合并。 --- ### 代码分析 ```java int getTotalMemLen4Init(List<RangeOfMem> cmdsOfMemInit) { BitSet bitSet = new BitSet(); cmdsOfMemInit.stream().forEach(e -> bitSet.set(e.start, e.end)); return bitSet.cardinality(); } ``` ### 逐行解释 1. `BitSet bitSet = new BitSet();` - 创建一个 `BitSet` 对象。`BitSet` 是 Java 提供的一个位集合类,可以高效地存储布尔状态(0 或 1)。 - 在这里,它被用来记录哪些内存地址已经被“设置”(即被初始化)。 2. `cmdsOfMemInit.stream().forEach(e -> bitSet.set(e.start, e.end));` - 遍历传入的内存初始化指令列表 `cmdsOfMemInit`。 - 对于每个 `RangeOfMem` 对象 `e`,调用 `bitSet.set(e.start, e.end)`。 - `set(start, end)` 方法会将从 `start` 到 `end - 1` 的所有位设置为 `true`。 - 注意:`set(start, end)` 是左闭右开区间,即 `[start, end)`。 3. `return bitSet.cardinality();` - `cardinality()` 方法返回 `BitSet` 中为 `true` 的位数,即总共被设置的内存地址数量。 - 这个值就是所有内存初始化指令中**去重后的总内存地址数量**。 --- ### 示例 假设 `RangeOfMem` 是如下定义的类: ```java class RangeOfMem { int start; int end; } ``` 并假设传入的 `cmdsOfMemInit` 包含以下两个范围: ```java [ new RangeOfMem(0, 10), new RangeOfMem(5, 15) ] ``` - 第一个范围:0~9(共10个地址) - 第二个范围:5~14(共10个地址) - 合并后:0~14(共15个地址) 所以 `bitSet.cardinality()` 返回 `15`。 --- ### 优点 - **自动去重**:`BitSet.set(start, end)` 会自动处理地址范围的重叠,避免重复计算。 - **高效**:使用位操作,效率高,适合处理大量连续地址的范围。 --- ### 注意事项 - `BitSet` 的性能和内存占用取决于最大设置的地址值。如果地址非常大(例如 `e.end` 是 `Integer.MAX_VALUE`),可能会导致内存占用过高。 - 如果内存地址是稀疏分布的,可以考虑使用 `java.util.BitSet` 的替代方案(如 `Eclipse Collections` 或 `RoaringBitmap`)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值