[毒瘤题]玛里苟斯:线性基,表达式分析,测试点分治

魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。

S 是一个可重集合,S={a1,a2,…,an}。等概率随机取 S 的一个子集 A={ai1,…,aim}。

计算出 A 中所有元素异或和 x, 求 xk 的期望。

如果结果是整数,直接输出。如果结果是小数(显然这个小数是有限的),输出精确值(末尾不加多余的 0)。

1≤n≤100000,1≤k≤5,ai≥0。最终答案小于 263 。k=1,2,3,4,5 各自占用 20% 的数据

 

可视的题面一直不在线。。。求xk的期望也是惊了。

但是这是大神题。。。同时也是傻逼题。

说它傻逼是因为出题人&数据真是毒瘤到极致。

首先点明几个坑点:

1)这题正解就是测试点分治。

2)不允许任何精度误差,见输出格式

3)虽然保证答案不爆long long,但是中间运算量会爆

出题人又反人类了。

首先我们明确两个本题的特点:

一个是在albus就是要第一个出场那道题里就用到的结论。

另一个是,因为最终的答案不会超过263,而你的计算是要做k次方的,所以输入的数不会超过264/k

还有虽然让你输出精确小数,但是由第一条结论,所以其实答案撑死是一个.5结尾。

 

好,开始讲。

 

先考虑分数最多而相对简单的k>=3,由第二条结论,这时候线性基里的数不超过21个。

那么就可以状压枚举线性基可以表示的所有数了,运用结论一,它们是等概率的。

概率也可以直接算,貌似挺简单?

爆零惊喜!!!

因为ans一直在累加,我说过。。。它中间运算量炸long long了。。。

所以我手动模拟了一下__int128(毕竟考试不让用),开两个变量,以1<<cnt为进制手动做伪高精。

 

k=1的点也勉强可以做??分析一下吧。

因为每一种子集都会被考虑到,所以考虑我们逐位考虑单独的贡献。

如果所有数的这一位都是0,那么所有的异或和结果都是0。

只要有些数的这一位是1,那么不管到底有几个数,选奇数个的概率和选偶数个完全相等,所以贡献是这一位/2。

那么整个数的贡献就是所有数的或和的一半。

你以为你拿到20分了?

15分惊喜!!!

我再说一遍,答案不超过263,不代表计算量不会爆。

因为你最后除2了,所以之前你会得到264

然后你要是开long long的话就负了。。。

跪模(裱)出题人。

 

好吧,你活过来了。

还差最后一个k=2。

二次方,不能状压,也不能想刚才那么简单做,怎么办?

平方式展开还是经常用的。

这时候线性基有32位数。

我们不断枚举每一位的贡献肯定是不够的了,所以我们枚举两位。

在任意子集中考虑两位,它们的贡献是2i2jbibj,b表示异或和二进制下这一位是1还是0。

和k=1类似我们考虑所有情况(因为每种子集都会出现)

如果两位数都没有在这n个数字里出现过,那么显然没有贡献。

接下来考虑在选出的子集里,第i位为1第j位为0的有a个,反之的有b个,都为1的有c个。

把它们异或起来的结果就是(a+c&1,b+c&1)。

 

如果那个a类数有0个,b类数有0个,那么你只在c类数里面选,上面说过选出奇数个偶数个的概率相等,所以此时有1/2的概率贡献答案。

 

如果a类有0个,b类有>0个,那么

1)如果c是奇数,为了有贡献,b必须是偶数,概率是1/2×1/2=1/4

2)如果c是偶数,那么第j位一定没贡献,所以贡献的概率是0

所以a类有0个,b类有>0个时贡献的概率是1/4

 

如果a类有>0个,b类有0个,同理也是1/4

 

如果ab都>0。

1)选偶数个c。那么ab都是奇数。贡献的概率是1/2×1/2×1/2=1/8

2)选奇数个c。那么ab都是偶数。贡献的概率是1/2×1/2×1/2=1/8

那么在ab都大于0时贡献的总概率是1/8+1/8=1/4

 

可以合并一下,就是如果ab都不存在贡献的概率就是1/2,否则就是1/4。

然后就是代码实现的问题了,求个期望就好了。

终于可以AC了。

爆零惊喜!!!

中间运算量有一次爆了long long。而且我位运算的左移写的是1<<x而不是1ll<<x。。。

 

目前为止见过的最毒瘤。。。

在颓了一半题解之后又WA了一页之后终于干掉了。。。

 1 #include<cstdio>
 2 #define int unsigned long long
 3 int n,k,base[65],x[100005],chg[65],A,ansx,ansy,cnt;
 4 main(){
 5     scanf("%llu%llu",&n,&k);
 6     for(int i=1;i<=n;++i){
 7         scanf("%llu",&x[i]);int X=x[i];A|=x[i];
 8         for(int i=62;~i;--i)if(X&1ll<<i&&!base[i]){base[i]=X;break;}
 9             else if(X&1ll<<i)X^=base[i];
10     }
11     if(k==1)printf("%llu",A>>1),puts(A&1?".5":"");
12     else if(k==2){
13         for(int j=0;j<32;++j)for(int t=0;t<32;++t)if(A>>j&1&&A>>t&1){
14             int f=0;for(int i=1;i<=n;++i)if(x[i]>>j&1^x[i]>>t&1){f=1;break;}
15             if(t+j<1+f)ansy++;else ansx+=1ll<<t+j-f-1;
16         }
17         printf("%llu",ansx+(ansy>>1));puts(ansy&1?".5":"");
18     }else{
19         for(int i=22;~i;--i)if(base[i])chg[++cnt]=base[i];
20         for(int i=0;i<1<<cnt;++i){
21             int tot=0;
22             for(int j=0;j<cnt;++j)if(i&1<<j)tot^=chg[j+1];
23             int x=0,y=1;
24             for(int s=0;s<k;++s)x*=tot,y*=tot,x+=y>>cnt,y&=(1<<cnt)-1;
25             ansx+=x;ansy+=y;ansx+=ansy>>cnt;ansy&=(1<<cnt)-1;
26         }
27         printf("%llu",ansx);puts(ansy?".5":"");
28     }
29 }
View Code
posted @ 2019-10-06 07:27  DeepinC  阅读(347)  评论(1编辑  收藏  举报