[毒瘤题]玛里苟斯:线性基,表达式分析,测试点分治
魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。
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 }