zoukankan      html  css  js  c++  java
  • bzoj 3811 玛里苟斯

    题目传送门

      传送门I

      传送门II

    题目大意

      给定集合$S$,问集合$S$的任意选一个子集的异或和的$k$次幂期望。

      保证答案在$2^{63}$内。

      注意到答案在$2^{63}$内,所以,当$k geqslant 3$的时候,$a_{i} leqslant 2^{21}$,这意味着本质不同的异或结果至多$2^{21}$个。

      于是可以做线性基,然后暴力枚举子集计算异或和$k$次幂。

      注意答案在$2^{63}$内,但是中间结果会溢出。所以用两个unsigned long long来压成__int128。

      恰好除数为$2^{left | mathfrak{B} ight |}$。可以保留$left lfloor frac{ans}{2^{left | mathfrak{B} ight |}} ight floor$,以及除以$2^{left | mathfrak{B} ight |}$的余数。

      现在考虑$k < 3$的情况。

    • 如果$k = 1$,分别考虑每一位,如果这一位存在1个数为1,那么异或后这一位为1的概率为$frac{1}{2}$,因为选了多少个这一位为0个数不会影响,有影响的只是选了这一位为$1$的数的奇偶性。记得之前某篇博客里证明过,非空集合的奇子集的数量等于偶子集的数量。但是如果不存在这一位为1的数,那么结果为$0$。
    • 如果$k = 2$。那么对于一个异或和$x = b_{p}b_{p - 1}cdots b_{0}$,它的贡献为$sum_{i = 0}^{p}sum_{j = 0}^{p} b_{i} cdot b_{j} cdot 2^{i + j}$。
      然后考虑枚举任意两个二进制位,考虑它们对答案的贡献。
      接着需要做的事是,考虑选取一个子集,它的异或和在第$i$位和第$j$位上的值同时为1的概率(否则没有贡献)。
      如果第$i$位和第$j$位上,如果其中一个不存在一个数在这一位上为1,那么概率为0。
      如果第$i$位和第$j$位上,不满足上面的条件,但所有数这两位上的值都相等,那么概率为$frac{1}{2}$。
      如果以上两种情况都不满足。考虑对于每一个数将这两位上的值看成一个二元组$(b_{i}, b_{j})$,当且仅当它为$(1, 1)$时,才对答案有贡献。
      1.考虑选出的$(1, 1)$的个数为奇数,它的概率为$frac{1}{2}$,那么选出的$(0, 0)$的数量不限,$(0, 1)$的个数为奇数,$(1, 0)$的个数为奇数,它们的概率分别为$frac{1}{2}$,这一部分总的概率是$1 imes left(frac{1}{2} ight)^{3} = frac{1}{8}$。
      2.考虑选出的$(1, 1)$的个数为偶数,这一部分的概率也是$frac{1}{8}$。
      所以这样的概率总的为$frac{1}{8} imes 2 = frac{1}{4}$。

      由于异或的最后一位为1的概率为$frac{1}{2}$或者$0$,因此,结果的小数位要么是0,要么是.5。

      然后这道题要用unsigned long long存答案,不然会WA。

    Code

      1 /**
      2  * bzoj
      3  * Problem#3811
      4  * Accepted
      5  * Time: 1256ms
      6  * Memory: 2076k
      7  */
      8 #include <bits/stdc++.h>
      9 #ifdef WIN32
     10 #define Auto "%I64u"
     11 #else
     12 #define Auto "%llu"
     13 #endif
     14 using namespace std;
     15 typedef bool boolean;
     16 
     17 #define ll unsigned long long
     18 
     19 int n, k;
     20 ll *ar;
     21 
     22 inline void init() {
     23     scanf("%d%d", &n, &k);
     24     ar = new ll[(n + 1)];
     25     for (int i = 1; i <= n; i++)
     26         scanf(Auto, ar + i);
     27 }
     28 
     29 namespace small {
     30 
     31     ll res = 0, ans = 0;
     32     inline void solve() {
     33         if (k == 1)    {
     34             for (int i = 1; i <= n; i++)
     35                 res |= ar[i];
     36             printf(Auto, res >> 1);
     37             (res & 1) ? (puts(".5")) : (0);
     38         } else {
     39             for (int i = 0; i < 32; i++)
     40                 for (int j = 0; j < 32; j++) {
     41                     boolean aflag = false;
     42                     for (int k = 1; k <= n && !aflag; k++)
     43                         if (ar[k] & (1ll << i))
     44                             aflag = true;
     45                     if (!aflag)    continue;
     46                     aflag = false;
     47                     for (int k = 1; k <= n && !aflag; k++)
     48                         if (ar[k] & (1ll << j))
     49                             aflag = true;
     50                     if (!aflag)    continue;
     51                     aflag = false;
     52                     for (int k = 1; k <= n && !aflag; k++)
     53                         if (((ar[k] >> i) & 1) != ((ar[k] >> j) & 1))                                    aflag = true;
     54                     int p = i + j - aflag - 1;
     55                     if (p < 0)
     56                         res++;
     57                     else
     58                         ans += 1ll << p;
     59                 }
     60             ans += (res >> 1), res &= 1; 
     61             printf(Auto, ans);
     62             (res) ? puts(".5") : 0;
     63         }
     64     }
     65 
     66 }
     67 
     68 namespace big {
     69     const int maxbase = 23;
     70     ll br[maxbase];
     71     vector<ll> v;
     72     ll ans = 0, res = 0;
     73 
     74     inline void solve() {
     75         for (int i = 1; i <= n; i++)
     76             for (int j = maxbase - 1; ~j && ar[i]; j--) {
     77                 if ((ar[i] >> j) & 1) {
     78                     if (br[j])
     79                         ar[i] ^= br[j];
     80                     else
     81                         br[j] = ar[i], ar[i] = 0;
     82                 }
     83             }
     84         for (int i = 0; i < maxbase; i++)
     85             if (br[i])
     86                 v.push_back(br[i]);
     87         int s = (signed)v.size();
     88         ll mask = (1ull << s) - 1;
     89         for (int i = 0; i <= mask; i++) {
     90             ll xs = 0;
     91             for (int j = 0; j < s; j++)
     92                 if (i & (1 << j))
     93                     xs ^= v[j];
     94             ll a = 0, b = 1;
     95             for (int i = 0; i < k; i++) {
     96                 a = a * xs, b = b * xs;
     97                 a += (b >> s), b &= mask;
     98             }
     99 
    100             ans += a, res += b;
    101             ans += (res >> s), res &= mask;
    102         }
    103         printf(Auto, ans);
    104         puts((res) ? (".5") : (""));
    105     }
    106 }
    107 
    108 int main() {
    109     init();
    110     if (k < 3)
    111         small::solve();
    112     else
    113         big::solve();
    114     return 0;
    115 }
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/yyf0309/p/8698413.html
Copyright © 2011-2022 走看看