算法刷题之字典树考试

63 阅读1分钟

记录

题目来源

蓝桥杯官网的,题目链接

题意

表达式 i=1nj=i+1nf(Ai & Aj)\sum_{i=1}^{n} \sum_{j=i+1}^{n} f(A_{i}\ \& \ A_{j} ) ,将内层展开得i=1n(A1&A2+A1&A3+A1&A4+...+A1&An)\sum_{i=1}^{n}(A_{1} \& A_{2} + A_{1} \& A_{3} + A_{1} \& A_{4} + ... + A_{1} \& A_{n}),在展开就是

A1&A2+A1&A3+A1&A4+A1&A5+...+A1&An+A2&A3+A2&A4+A2&A5+...+A2&AnA_{1} \& A_{2} + A_{1} \& A_{3} + A_{1} \& A_{4} + A_{1} \& A_{5} + ... +A_{1} \& A_{n} +\\ A_{2} \& A_{3} +A_{2} \& A_{4} + A_{2} \& A_{5} +... + A_{2} \& A_{n}

意思就是将数组中的每两项进行与操作,对他们进行求和

思路

讲解视频上说的是贡献法,之前碰到了子串分值和子串分值和的题目,也是用贡献法来解决的,大佬们的解释是只取对答案有正面作用的结果,反面作用的不考虑

举个例子,下面的数字是二进制形式

1      1      0     10      1      0     11      1      1     11      0      0     11 \ \ \ \ \ \ 1 \ \ \ \ \ \ 0 \ \ \ \ \ 1 \\ 0 \ \ \ \ \ \ 1 \ \ \ \ \ \ 0 \ \ \ \ \ 1 \\ 1 \ \ \ \ \ \ 1 \ \ \ \ \ \ 1 \ \ \ \ \ 1 \\ 1 \ \ \ \ \ \ 0 \ \ \ \ \ \ 0 \ \ \ \ \ 1 \\

对于第一位(从右往左数的第一位)一共有4个1,从中选两个出来,一共有几种情况?组合数喽,C42C_{4}^{2} 对于第二位一共有1个1,选不出来两个那就是0 对于第三位一共有3个1,能选出来C32C_{3}^{2} 对于第四位一共有3个1,能选出来C32C_{3}^{2} 思路就是将数组中每一个元素的1的位置存储起来,例如位权为202^{0}上有几个1,若1的个数大于等于2,就算组合数Cn2C_{n}^{2}即可。这里我描述的不太清楚,看代码可能会好理解点

代码

import java.util.*;
public class Main {
    // 算组合数的,那个板子不太记得了,直接模拟
    public static double one_Cnt(long m){
        double res = 1;
        int cnt =2;
        while (cnt!=0){
            res = res*m;
            m--;
            cnt--;
        }
        res/=2; //因为是从n中选两个,所以分母固定就是2
        return res;
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int len = s.nextInt();
        int[] data = new int[len];
        long res =0;//存储结果的,选long类型是可能会爆int
        for(int i=0;i<data.length;i++)
        {
            data[i] = s.nextInt();
        }
        // number[i] 表示第i位上A1-An的二进制表示中有多少个1
        long[] number = new long[33];
        for(int i=31;i>=0;i--){//题目保证数据范围是在int类型内,int类型32位,最多只需要右移31位
            for(int j=0;j<len;j++)
                // 位运算技巧,如果你想取得数字a的第2位数字,那么只用将a右移1位再与一下,或者和0异或一下也行
                number[i] = number[i] + ((data[j]>>i) & 1);
        }

        for(int i=31;i>=0;i--){
            if(number[i]>=2){
                res+=one_Cnt(number[i]);
            }
        }
        System.out.println(res);
        s.close();
    }
}