zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:Race(数学+Trie树)

    题目描述

    一年一度的运动会开始了。有$N$个选手参赛,第$i$个选手有一个能力值(保证$A[i]$两两不同),比赛一共进行了天。在第$j$天($0leqslant jleqslant 2^{m-1}$)的比赛中,第$i$个选手的得分为$A[i] xor j$,然后从大到小排名,排名为$x$($x$从$0$开始)的同学会获得的积分,你需要求出每个同学最后总的积分和$q[i]$模${10}^9+7$的结果$p[i]$。为了避免输出文件过大,你只要输出$p[i]$的异或和即可。


    输入格式

    第一行两个整数,分别代表$N,M$。
    接下来一行$N$个整数,第$i$个数代表$A[i]$。


    输出格式

    一个整数代表答案。


    样例

    样例输入:

    3 2
    0 1 2

    样例输出:

    8


    数据范围与提示

    对于$10\%$的数据,$Mleqslant 5$。
    对于$30\%$的数据,$Nleqslant 100$。
    对于$50\%$的数据,$Nleqslant 1,000$。
    对于$100\%$的数据,$Nleqslant 200,000,Mleqslant 30,A[i]<2^m$。


    题解

    考虑怎么求出$x$的答案。平方相当于是枚举两个人(可以相同),把这两个人同时排在$x$前面的天数计入答案。那么对于$x$,如果我们求出$f[i]$也就是能力值的二进制中第$i+1$到$M-1$位都和他相等且第$i$位不同的人有多少个,那么这些人是否排在他前面只由第$i$位确定,一共有$2^{(M-1)}$天,而且不需要从这些人中枚举两个人了,因为直接平方即可。我们只要枚举$i$和$j$,$f[i]$这些人和$f[j]$这些人同时排在他前面的天数为$2^{(M-2)}$,所以把$2 imes f[i] imes f[j] imes 2^{(M-2)}$计入答案。具体实现有很多方法,可以用$trie$树实现。

    时间复杂度:$Theta(N)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    int trie[10000000][2],cnt;
    long long size[10000000];
    long long ans;
    void insert(int x)
    {
    	int p=0;
    	for(int i=m-1;i>=0;i--)
    	{
    		int c=(x>>i)&1;
    		if(!trie[p][c])trie[p][c]=++cnt;
    		p=trie[p][c];
    		size[p]++;
    	}
    }
    void dfs(int x,long long sum,long long num)
    {
    	if(trie[x][0])dfs(trie[x][0],(sum+(size[trie[x][1]]*size[trie[x][1]]%1000000007*(1<<m-1)%1000000007+size[trie[x][1]]*num%1000000007*(1<<m-1)%1000000007)%1000000007)%1000000007,num+size[trie[x][1]]);
    	if(trie[x][1])dfs(trie[x][1],(sum+(size[trie[x][0]]*size[trie[x][0]]%1000000007*(1<<m-1)%1000000007+size[trie[x][0]]*num%1000000007*(1<<m-1)%1000000007)%1000000007)%1000000007,num+size[trie[x][0]]);
    	if(!trie[x][0]&&!trie[x][1])ans^=sum;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		int a;scanf("%d",&a);
    		insert(a);
    	}
    	dfs(0,0,0);
    	printf("%lld",ans);
    	return 0;
    }

    rp++

  • 相关阅读:
    HDU 1106 排序
    strtok函数()
    HDU 2187汶川地震
    HDU 1716 排列2
    Rightmost Digit
    Text Reverse
    快速幂
    插入排序的一个应用-调整负数在前,正数在后,原来相对位置不变
    cuda 5.0配置vs2008+Visual Assist X +安装问题解决
    vc 热键、组合键的用法
  • 原文地址:https://www.cnblogs.com/wzc521/p/11566898.html
Copyright © 2011-2022 走看看