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

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

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 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 struct bigint{
 6     int a[102],ws;
 7     void r(){
 8         for(int i=ws-1;i;--i)if(a[i]&1)a[i-1]+=10;
 9         for(int i=ws-1;~i;--i)a[i]>>=1;
10         if(ws&&!a[ws-1])ws--;
11     }
12     int A(){return a[0]&1;}
13 }n;
14 char s[105];int k;const int mod[4]={1,10,100,1000};
15 void mult(long long &a,long long b){long long modd=1000000000;
16     long long a1=a/modd,a2=a%modd,b1=b/modd,b2=b%modd;
17     b=a2*b2;a=(a1*b2+a2*b1)%modd;a=a*modd+b;
18     while(a&&a%10==0)a/=10;
19 }
20 int main(){//freopen("ex_num2.in","r",stdin);freopen("my.out","w",stdout);
21     int t,l;long long ans,res;scanf("%d",&t);//long long A=2000000001;mult(A,A);printf("%lld\n",A);
22     while(t--){
23         scanf("%s%d",s,&k);l=strlen(s);
24         ans=1;res=0;
25         for(int i=max(0,l-k-k);i<=l-1;++i)res=res*10+s[i]-48;
26         for(int i=1;i<=res;++i)mult(ans,i);
27         res=1;for(int i=1;i<=mod[k];++i)mult(res,i);
28         n.ws=max(0,l-k-k);
29         for(int i=0;i<n.ws;++i)n.a[i]=s[l-i-k-k-1]-48;
30         for(;n.ws;n.r(),mult(res,res))if(n.A())mult(ans,res);
31         ans%=mod[k];
32         for(int i=k;i;--i)if(ans/mod[i-1]==0)putchar('0');else break;if(ans)printf("%lld\n",ans);
33     }
34 }
View Code

猜你喜欢

转载自www.cnblogs.com/hzoi-DeepinC/p/11625285.html