证明均来自xht37 的洛谷博客

作用

在 \(OI\) 中,\(FWT\) 是用于解决对下标进行位运算卷积问题的方法。

\(c_{i}=\sum_{i=j \oplus k} a_{j} b_{k}\)

其中 \(\oplus\) 是二元位运算中的一种。

实现

\(or\) 运算

构造 \(fwt[a]_i = \sum_{j|i=i} a_j\)

\(\begin{aligned} fwt[a] \times fwt[b] &= \left(\sum_{j|i=i} a_j\right)\left(\sum_{k|i=i} b_k\right) \\\\ &= \sum_{j|i=i} \sum_{k|i=i} a_jb_k \\\\ &= \sum_{(j|k)|i = i} a_jb_k \\\\ &= fwt[c] \end{aligned}\)

从 \([a]\) 到 \(fwt[a]\) 可以分治解决

我们从小到大依次枚举长度为 \(2^i\) 的区间

设最高位为第 \(i\) 位

此时我们已经求出了前 \(i-1\) 位的贡献

并且区间的左半部分最高位上的数字为 \(0\)

区间的右半部分最高位上的数字为 \(1\)

对于左边的这些数,右边的数显然不可能是左边的数的子集

只能由自己 \(i-1\) 位的贡献转移过来

但是左边的数会给相应位置的右边的数做出贡献

因此我们在进行变换的时候要把这个贡献加上

同样在进行逆变换的时候相应地减去即可

代码

  1. void fwtor(rg int A[],rg int typ){
  2. for(rg int k=1,o=2;o<=mmax;k<<=1,o<<=1){
  3. for(rg int j=0;j<mmax;j+=o){
  4. for(rg int i=0;i<k;i++){
  5. A[i+j+k]+=typ*A[i+j];
  6. A[i+j+k]=getmod(A[i+j+k]);
  7. }
  8. }
  9. }
  10. }

\(and\) 运算

和 \(or\) 运算基本一样,只是这次变成了右区间对左区间有贡献

代码

  1. void fwtand(rg int A[],rg int typ){
  2. for(rg int k=1,o=2;o<=mmax;k<<=1,o<<=1){
  3. for(rg int j=0;j<mmax;j+=o){
  4. for(rg int i=0;i<k;i++){
  5. A[i+j]+=typ*A[i+j+k];
  6. A[i+j]=getmod(A[i+j]);
  7. }
  8. }
  9. }
  10. }

\(xor\) 运算

这种运算比较复杂,因为不再是简单的子集的关系了

但是我们仍然可以用以上两种运算的思想

定义 \(x\otimes y=\text{popcount}(x \& y) \bmod 2\)

其中 \(\text{popcount}\) 表示「二进制下 \(1\) 的个数」

满足 \((i \otimes j) \operatorname{xor} (i \otimes k) = i \otimes (j \operatorname{xor} k)\)

构造 \(fwt[a]_i = \sum_{i\otimes j = 0} a_j - \sum_{i\otimes j = 1} a_j\)

则有

\(\begin{aligned} fwt[a] \times fwt[b] &= \left(\sum_{i\otimes j = 0} a_j - \sum_{i\otimes j = 1} a_j\right)\left(\sum_{i\otimes k = 0} b_k - \sum_{i\otimes k = 1} b_k\right) \\ &=\left(\sum_{i\otimes j=0}a_j\right)\left(\sum_{i\otimes k=0}b_k\right)-\left(\sum_{i\otimes j=0}a_j\right)\left(\sum_{i\otimes k=1}b_k\right)-\left(\sum_{i\otimes j=1}a_j\right)\left(\sum_{i\otimes k=0}b_k\right)+\left(\sum_{i\otimes j=1}a_j\right)\left(\sum_{i\otimes k=1}b_k\right) \\ &=\sum_{i\otimes(j \operatorname{xor} k)=0}a_jb_k-\sum_{i\otimes(j\operatorname{xor} k)=1}a_jb_k \\ &= fwt[c] \end{aligned}
\)

当最高位是 \(0\) 时,因为 \(0\&1=0\),\(0\&0=0\)

二进制下 \(1\) 的个数不变

所以左边区间的价值应为只考虑前 \(i-1\) 位时左边区间的价值加上只考虑前 \(i-1\) 位时右边区间的价值

而对于右边区间,当 \(1\&1=1\) 时,二进制下一的个数会发生变化

所以应该是只考虑前 \(i-1\) 位时左边区间的价值减去只考虑前 \(i-1\) 位时右边区间的价值

逆变换就是反这来,乘上 \(\frac{1}{2}\) 即可

代码

  1. void fwtxor(rg int A[],rg int typ){
  2. for(rg int k=1,o=2;o<=mmax;k<<=1,o<<=1){
  3. for(rg int j=0;j<mmax;j+=o){
  4. for(rg int i=0;i<k;i++){
  5. rg int x=1LL*A[i+j]*typ%mod,y=1LL*A[i+j+k]*typ%mod;
  6. A[i+j]=getmod(x+y);
  7. A[i+j+k]=getmod(x-y);
  8. }
  9. }
  10. }
  11. }

题目

P5366 [SNOI2017]遗失的答案

题目传送门

分析

先特判掉 \(G\) 不能整除 \(L\) 的情况

然后把 \(L\) 和 \(n\) 同时除以 \(G\)

这样问题就转化为了在 \(1\) 到 \(n\) 中选择一些数

使得他们的最大公因数为 \(1\),最小公倍数为 \(L\)

将 \(L\) 进行质因数分解,设 \(L=p_1^{a_1}p_2^{a_2}...p_n^{a_n}\)

如果要满足条件

那么对于任意一个质因数 \(p_i\) ,选择的数中必须至少存在一个数,使得它分解质因数后 \(p_i\) 的指数等于 \(a_i\)

同理,对于任意一个质因数 \(p_i\) ,选择的数中必须至少存在一个数,不含有 \(p_i\) 这个质因数

第一个条件可以看做是否满足上界,第二个条件可以看作是否满足下界

因为 \(L\) 小于等于 \(10^{8}\),所以最多含有 \(8\) 个不同的质因数

因此可以状压

设 \(11\) 表示同时满足上界和下界,\(10\) 表示只满足上界,\(01\) 表示只满足下界,\(00\) 表示上界和下界都不满足

显然满足条件的只能是 \(L\) 的因数,我们可以把 \(L\) 的所有因数都筛出来

然后求出这些因数所代表的状态

因数不会太多,最多只有 \(768\) 个

如果没有必须选择 \(x\) 的限制,那么直接设 \(f[i][j]\) 表示考虑前 \(i\) 个数,状态为 \(j\) 的方案数进行 \(dp\) 即可

如果考虑 \(x\) 的限制,我们就需要维护一个前缀 \(dp\) 数组 \(pre\) 和后缀 \(dp\) 数组 \(suf\)

对于第 \(i\) 个数,我们把 \(pre[i-1]\) 和 \(suf[i+1]\) 进行或运算卷积

最后只要第 \(i\) 个数的状态与某个状态进行或运算等于全集

那么就可以累加这个状态的答案

代码

  1. #include<cstdio>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<algorithm>
  5. #define rg register
  6. inline int read(){
  7. rg int x=0,fh=1;
  8. rg char ch=getchar();
  9. while(ch<'0' || ch>'9'){
  10. if(ch=='-') fh=-1;
  11. ch=getchar();
  12. }
  13. while(ch>='0' && ch<='9'){
  14. x=(x<<1)+(x<<3)+(ch^48);
  15. ch=getchar();
  16. }
  17. return x*fh;
  18. }
  19. const int mod=1e9+7,maxn=70005,maxm=1005;
  20. int n,g,l,q,x,mmax;
  21. int getmod(rg int now){
  22. return now>=mod?now-mod:now<0?now+mod:now;
  23. }
  24. void fwtor(rg int A[],rg int typ){
  25. for(rg int o=2,k=1;o<=mmax;o<<=1,k<<=1){
  26. for(rg int i=0;i<mmax;i+=o){
  27. for(rg int j=0;j<k;j++){
  28. A[i+j+k]+=A[i+j]*typ;
  29. A[i+j+k]=getmod(A[i+j+k]);
  30. }
  31. }
  32. }
  33. }
  34. int pri[maxn],mi[maxn];
  35. void divid(rg int now){
  36. rg int m=sqrt(now),ncnt=0;
  37. for(rg int i=2;i<=m;i++){
  38. if(now%i==0){
  39. ncnt=0;
  40. pri[++pri[0]]=i;
  41. while(now%i==0){
  42. now/=i;
  43. ncnt++;
  44. }
  45. mi[pri[0]]=ncnt;
  46. }
  47. }
  48. if(now>1){
  49. pri[++pri[0]]=now;
  50. mi[pri[0]]=1;
  51. }
  52. }
  53. int sta[maxn],tp,zt[maxn];
  54. void getit(){
  55. rg int m=sqrt(l);
  56. for(rg int i=1;i<=m;i++){
  57. if(l%i==0){
  58. if(i<=n) sta[++tp]=i;
  59. if(i*i!=l && l/i<=n) sta[++tp]=l/i;
  60. }
  61. }
  62. }
  63. int pre[maxm][maxn],suf[maxm][maxn],tmp[maxn],ans[maxn];
  64. int getzt(rg int now){
  65. rg int zt0=0,zt1=0;
  66. for(rg int i=1;i<=pri[0];i++){
  67. rg int ncnt=0;
  68. while(now%pri[i]==0){
  69. now/=pri[i];
  70. ncnt++;
  71. }
  72. if(ncnt==0) zt0|=(1<<(i-1));
  73. else if(ncnt==mi[i]) zt1|=(1<<(i-1));
  74. }
  75. return zt0|(zt1<<pri[0]);
  76. }
  77. int main(){
  78. n=read(),g=read(),l=read(),q=read();
  79. if(l%g){
  80. for(rg int i=1;i<=q;i++){
  81. x=read();
  82. printf("0\n");
  83. }
  84. } else {
  85. l/=g,n/=g;
  86. divid(l);
  87. mmax=1<<(2*pri[0]);
  88. getit();
  89. std::sort(sta+1,sta+1+tp);
  90. for(rg int i=1;i<=tp;i++) zt[i]=getzt(sta[i]);
  91. pre[0][0]=suf[tp+1][0]=1;
  92. for(rg int i=1;i<=tp;i++){
  93. memcpy(pre[i],pre[i-1],sizeof(pre[i-1]));
  94. for(rg int j=0;j<mmax;j++){
  95. pre[i][j|zt[i]]=getmod(pre[i][j|zt[i]]+pre[i-1][j]);
  96. }
  97. }
  98. for(rg int i=tp;i>=1;i--){
  99. memcpy(suf[i],suf[i+1],sizeof(suf[i+1]));
  100. for(rg int j=0;j<mmax;j++){
  101. suf[i][j|zt[i]]=getmod(suf[i][j|zt[i]]+suf[i+1][j]);
  102. }
  103. }
  104. for(rg int i=0;i<=tp+1;i++){
  105. fwtor(pre[i],1);
  106. fwtor(suf[i],1);
  107. }
  108. for(rg int i=1;i<=tp;i++){
  109. for(rg int j=0;j<mmax;j++){
  110. tmp[j]=1LL*pre[i-1][j]*suf[i+1][j]%mod;
  111. }
  112. fwtor(tmp,-1);
  113. for(rg int j=0;j<mmax;j++){
  114. if((zt[i]|j)==mmax-1) ans[i]=getmod(ans[i]+tmp[j]);
  115. }
  116. }
  117. for(rg int i=1;i<=q;i++){
  118. x=read();
  119. if(x%g) printf("0\n");
  120. else {
  121. x/=g;
  122. if(l%x) printf("0\n");
  123. else {
  124. rg int wz=std::lower_bound(sta+1,sta+1+tp,x)-sta;
  125. printf("%d\n",ans[wz]);
  126. }
  127. }
  128. }
  129. }
  130. return 0;
  131. }

P3175 [HAOI2015]按位或

题目传送门

分析

要用到 \(min\)−\(max\) 容斥

不会的可以看我的容斥原理学习笔记

$max(S)=\sum_{T\subseteq S}(-1)^{|T|+1}min(T) $

$min(S)=\sum_{T\subseteq S}(-1)^{|T|+1}max(T) $

设 \(max(S)\) 为 \(S\) 中最晚的元素出现的期望次数

设 \(min(S)\) 为 \(S\) 中最早的元素出现的期望次数

问题转换为如何求 \(min(T)\)

设 \(P=\sum\limits_{S\subseteq\complement_UT}P(S)\)

\(E(\min(T))=P\sum\limits^{+\infty}_{i=1}i(1-p)^{i-1}\)

有边是一个等比数列乘等差数列的求和公式

化简之后是

\(\frac{1-(1-P)^n}{P^2}-\frac{n(1-P)^n}{P}\)

当 \(n\) 趋进于无穷大时

\((1-P)^n\) 趋进于 \(0\)

因此最终的结果是 \(\frac{1}{P^2}\)

再乘上外面的一个 \(P\),就是 \(\frac{1}{P}\)

剩下的再用一个或运算卷积即可

代码

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #define rg register
  6. inline int read(){
  7. rg int x=0,fh=1;
  8. rg char ch=getchar();
  9. while(ch<'0' || ch>'9'){
  10. if(ch=='-') fh=-1;
  11. ch=getchar();
  12. }
  13. while(ch>='0' && ch<='9'){
  14. x=(x<<1)+(x<<3)+(ch^48);
  15. ch=getchar();
  16. }
  17. return x*fh;
  18. }
  19. const int maxn=2e6+5,mod=998244353;
  20. const double eps=1e-10;
  21. int n,mmax,siz[maxn];
  22. double a[maxn];
  23. void fwtor(rg double A[],rg int typ){
  24. for(rg int i=1;i<=n;i++){
  25. for(rg int j=0;j<mmax;j+=(1<<i)){
  26. for(rg int k=0;k<1<<(i-1);k++){
  27. A[j|(1<<(i-1))|k]+=A[j|k]*typ;
  28. }
  29. }
  30. }
  31. }
  32. int main(){
  33. n=read();
  34. mmax=1<<n;
  35. for(rg int i=0;i<mmax;i++){
  36. scanf("%lf",&a[i]);
  37. }
  38. fwtor(a,1);
  39. for(rg int i=0;i<mmax;i++){
  40. siz[i]=siz[i>>1]+(i&1);
  41. }
  42. rg double nans=0;
  43. for(rg int i=1;i<mmax;i++){
  44. if(1.0-a[(mmax-1)^i]<eps){
  45. printf("INF\n");
  46. return 0;
  47. }
  48. nans+=((siz[i]&1)?(1.0):(-1.0))/(1.0-a[(mmax-1)^i]);
  49. }
  50. printf("%.8f\n",nans);
  51. return 0;
  52. }

P5643 [PKUWC2018]随机游走

题目传送门

分析

同样是 \(min\)-\(max\) 容斥,先求出至少经过一个点的期望步数

然后再求出全部经过的期望步数

$max(S)=\sum_{T\subseteq S}(-1)^{|T|+1}min(T) $

设 \(f_i\) 表示从 \(i\) 出发,经过 \(S\) 中的至少一个点的期望步数

\(deg_i\) 为点 \(i\) 的度数,\(j\) 为 \(i\) 的儿子节点

可以得到这样的递推式:

\(f_i=\frac1{deg_i}(f_{fa_i}+\sum f_j)+1\)

设 \(f_i=k_if_{fa_i}+b_i\)

化简之后可以得到

$f_i=\frac1{deg_i-\sum k_j}f_{fa_i}+\frac{deg_i+\sum b_j}{deg_i-\sum k_j} $

$k_i=\frac 1{deg_i-\sum k_j},b_i=\frac{deg_i+\sum b_j}{deg_i-\sum k_j} $

这个东西可以 \(dfs\) 求出来

然后就可以用或运算卷积合并预处理出每一个集合的答案

代码

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #define rg register
  6. inline int read(){
  7. rg int x=0,fh=1;
  8. rg char ch=getchar();
  9. while(ch<'0' || ch>'9'){
  10. if(ch=='-') fh=-1;
  11. ch=getchar();
  12. }
  13. while(ch>='0' && ch<='9'){
  14. x=(x<<1)+(x<<3)+(ch^48);
  15. ch=getchar();
  16. }
  17. return x*fh;
  18. }
  19. const int maxn=1e6+5,maxm=25,mod=998244353;
  20. int n,q,x,mmax,h[maxm],k[maxm],a[maxm],tot=1,du[maxm],ans[maxn],siz[maxn];
  21. int ksm(rg int ds,rg int zs){
  22. rg int nans=1;
  23. while(zs){
  24. if(zs&1) nans=1LL*nans*ds%mod;
  25. ds=1LL*ds*ds%mod;
  26. zs>>=1;
  27. }
  28. return nans;
  29. }
  30. struct asd{
  31. int to,nxt;
  32. }b[maxm<<1];
  33. void ad(rg int aa,rg int bb){
  34. b[tot].to=bb;
  35. b[tot].nxt=h[aa];
  36. h[aa]=tot++;
  37. }
  38. int getmod(rg int now){
  39. return (now>=mod)?(now-mod):((now<0)?(now+mod):now);
  40. }
  41. void fwtor(rg int A[],rg int typ){
  42. for(rg int o=2,k=1;o<=mmax;o<<=1,k<<=1){
  43. for(rg int i=0;i<mmax;i+=o){
  44. for(rg int j=0;j<k;j++){
  45. A[i+j+k]+=A[i+j]*typ;
  46. A[i+j+k]=getmod(A[i+j+k]);
  47. }
  48. }
  49. }
  50. }
  51. void dfs(rg int now,rg int lat,rg int zt){
  52. if(zt&(1<<(now-1))) return;
  53. rg int ans1=0,ans2=0;
  54. for(rg int i=h[now];i!=-1;i=b[i].nxt){
  55. rg int u=b[i].to;
  56. if(u==lat) continue;
  57. dfs(u,now,zt);
  58. ans1+=k[u];
  59. ans2+=a[u];
  60. ans1=getmod(ans1);
  61. ans2=getmod(ans2);
  62. }
  63. k[now]=ksm(getmod(du[now]-ans1),mod-2);
  64. a[now]=1LL*k[now]*getmod(du[now]+ans2)%mod;
  65. }
  66. int main(){
  67. memset(h,-1,sizeof(h));
  68. n=read(),q=read(),x=read();
  69. rg int aa,bb,cc;
  70. for(rg int i=1;i<n;i++){
  71. aa=read(),bb=read();
  72. ad(aa,bb);
  73. ad(bb,aa);
  74. du[aa]++,du[bb]++;
  75. }
  76. mmax=1<<n;
  77. for(rg int i=0;i<mmax;i++) siz[i]=siz[i>>1]+(i&1);
  78. for(rg int i=0;i<mmax;i++){
  79. memset(k,0,sizeof(k));
  80. memset(a,0,sizeof(a));
  81. dfs(x,0,i);
  82. ans[i]=a[x]*((siz[i]&1)?1:(-1));
  83. ans[i]=getmod(ans[i]);
  84. }
  85. fwtor(ans,1);
  86. for(rg int i=1;i<=q;i++){
  87. aa=read();
  88. cc=0;
  89. for(rg int j=1;j<=aa;j++){
  90. bb=read();
  91. cc|=(1<<(bb-1));
  92. }
  93. printf("%d\n",ans[cc]);
  94. }
  95. return 0;
  96. }

快速沃尔什变换 (FWT)学习笔记的更多相关文章

  1. 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记

    一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...

  2. 快速沃尔什变换 FWT 学习笔记【多项式】

    〇.前言 之前看到异或就担心是 FWT,然后才开始想别的. 这次学了 FWT 以后,以后判断应该就很快了吧? 参考资料 FWT 详解 知识点 by neither_nor 集训队论文 2015 集合幂 ...

  3. 快速沃尔什变换(FWT)学习笔记 + 洛谷P4717 [模板]

    FWT求解的是一类问题:\( a[i] = \sum\limits_{j\bigoplus k=i}^{} b[j]*c[k] \) 其中,\( \bigoplus \) 可以是 or,and,xor ...

  4. 【学习笔鸡】快速沃尔什变换FWT

    [学习笔鸡]快速沃尔什变换FWT OR的FWT 快速解决: \[ C[i]=\sum_{j|k=i} A[j]B[k] \] FWT使得我们 \[ FWT(C)=FWT(A)*FWT(B) \] 其中 ...

  5. FMT/FWT学习笔记

    目录 FMT/FWT学习笔记 FMT 快速莫比乌斯变换 OR卷积 AND卷积 快速沃尔什变换(FWT/XOR卷积) FMT/FWT学习笔记 FMT/FWT是算法竞赛中求or/and/xor卷积的算法, ...

  6. FWT学习笔记

    FWT学习笔记 引入 一般的多项式乘法是这样子的: \(c_i=\sum_{i,j}a_j*b_k*[j+k==i]\) 但是如果我们将这个乘法式子里面的+号变换一下变成其他的运算符号呢? \(c_i ...

  7. 快速沃尔什变换FWT

    快速沃尔什变换\(FWT\) 是一种可以快速完成集合卷积的算法. 什么是集合卷积啊? 集合卷积就是在集合运算下的卷积.比如一般而言我们算的卷积都是\(C_i=\sum_{j+k=i}A_j*B_k\) ...

  8. FWT 学习笔记

    FWT学习笔记 好久以前写的,先粘上来 定义数组 \(n=2^k\) \(A=[a_0,a_1,a_2,a_3,...,a_{n-1}]\) 令\(A_0=[a_0,a_1,a_2,...,a_{\f ...

  9. 集合并卷积的三种求法(分治乘法,快速莫比乌斯变换(FMT),快速沃尔什变换(FWT))

    也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级 ...

随机推荐

  1. Mellanox 4036配置

    1.前言 内置factory-default 会重置所有参数到出厂设置. 内置reboot.拔电源就是重启. 外置reset就是重置芯片中数据,不会恢复到出厂设置. 2.感受下恢复出厂过程 4036- ...

  2. 帆软用工具测试超链接打开弹窗(iframe嵌套),解决js传参带中文传递有乱码问题

    1.新建超链接 随意点击一个单元格右击,选择 超级链接 2.在弹出的窗口中选择JavaScript脚本 如图: 其中红框框出的是几个要点   ,左边的就不讲了,右上角的参数cc是设置了公式remote ...

  3. JavaScript使用中的一些小技巧

    任何一门技术在实际中都会有一些属于自己的小技巧.同样的,在使用JavaScript时也有一些自己的小技巧,只不过很多时候有可能容易被大家忽略.而在互联网上,时不时的有很多同行朋友会总结(或收集)一些这 ...

  4. (九)ELF和动态链接

    前言: 我们都知道我们所写的程序是被编译为一条条的CPU指令去执行的,但是在linux系统下能够运行的程序在windows环境下却运行不起来,但是我们使用的CPU明明是一样的,这又是为什么呢? 一.程 ...

  5. Qt模型视图结构遇见的小问题

    在本文的最开始,我们来看两个帮助文档内容: selectionMode : SelectionMode This property holds which selection mode the vie ...

  6. PyQt(Python+Qt)学习随笔:QTabWidget选项卡部件的documentMode属性作用

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTabWidget的documentMode属性用于控制是否以适合文档页的模式呈现选项卡部件.这与 ...

  7. 模拟数组 push() 方法

    var array =[]; Array.prototype.push = function (){ for (var i=0; i< arguments.length; i++){ this[ ...

  8. vue props默认值国际化报错

    未做国际化处理 tabLabel: { type: Array, default: () => (["a", "b", "c"]) } ...

  9. Acwing 393. 雇佣收银员

    算法1: 差分约束 + 枚举 O(Tn2028) 由于牵扯到 \([i - 8 + 1, i]\) 这段区间的和的约束,所以用前缀和更好表达一些. 设 \(num[i]\)表示 \(i\) 时刻有多少 ...

  10. Java并发编程的艺术(十二)——并发容器和框架

    ConcurrentHashMap 为什么需要ConcurrentHashMap HashMap线程不安全,因为HashMap的Entry是以链表的形式存储的,如果多线程操作可能会形成环,那样就会死循 ...