• [CSP校内集训]race(位运算?)


    题意

    (n)个人,每个人有一个能力值(a_i)(a)互不相同),有(2^m)场比赛([0,2^m)),每场比赛一个人的得分(a_i oplus j),按照得分排序(得分每轮比赛清零),每个人获得排名(^2)积分(注意排名从0开始算),求每个人的积分(q\% (10^9+7))后的异或和,((nleq 200000,mleq 30))

    思路

    考场上的解法,并非最优解法qwq

    由于有取模操作,只能一个个人的处理,且可以看出是要一位位处理问题的

    假设当前处理到第i个人,设(k_j)为能力值在二进制下更高位与i一样,第j位不同的人数,(由这个定义可知(sum{k_j}=n-1),即除i外的每个人会且仅会被统计一次)


    暂时不管(k)怎么求,可以发现两个数之间的大小关系仅由从高向低第一个不同的位(即上面(k)的定义),对于一个(j),如果第(j)位和第(i)个人一样,那么他就比不过那(k_j)个人,即排名(+k_j),否则排名不会增加;而第(j)位是0或1的概率相等,所以第(i)个人取或不取(k_j)的概率相同,那么第(i)个人的积分为(0 + k_1^2 + k_2^2 +....+k_m^2 + (k_1+k_2)^2 + (k_1+k_2)^2 + .......(k_1+k_2+...+k_m)^2)(就像搜索m个数选或不选一样)

    好像写复杂了?不管不管

    这个式子可以化简,而我比较懒(cai),并没有化简完...

    [q=sum_{i=1}^{m} { k_i (2^{m-1}k_i + frac{sum_{j=1}^{m-1}{C_{m-1}^j imes j}}{m-i} imes(n-1-k_i)) } ]

    预处理一些东西之后这个式子可以(O(m))


    扯了那么多,到底怎么求(k)

    其实求(k)很简单,我的方法是对(a_i)排个序之后从高位到低位模拟即可,因为需要二分所以图方便用了个(lb),具体见代码

    时间复杂度(O(nlog^2n)),复杂度瓶颈为求(k),理论上可以过,实际上我自测的时候忘记关编译选项里面的(O2)了,结果跑了个0.6s还以为随便过。。。

    成功被卡常到2.9s(STL常数好大qwq),把(lb)换成手写二分即可0.5s过此题,和std速度差不多???

    正解是(O(nlogn))的trie,懒得打,看起来又是套路2333

    Code

    #include<bits/stdc++.h>
    #define N 200005
    #define re register
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    typedef long long ll;
    const ll mod = 1000000007;
    int n,m; ll a[N];
    ll k[35],ans=0;
    ll C[35][35],sum,po;
    
    template <class T>
    void read(T &x)
    {
    	char c; int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    ll quickpow(ll a,ll b)
    {
    	ll ret=1;
    	while(b)
    	{
    		if(b&1) ret=ret*a%mod;
    		a=a*a%mod;
    		b>>=1; 
    	}
    	return ret;
    }
    
    void init()//预处理一堆乱七八糟的东西 
    {
    	C[0][0]=C[1][0]=C[1][1]=1;
    	for(int i=2;i<=30;++i)
    	{
    		C[i][0]=1;
    		for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    	}
    	for(int i=1;i<m;++i) sum=(sum+C[m-1][i]*i%mod)%mod;
    	sum=sum*quickpow(m-1,mod-2)%mod;
    	po=quickpow(2,m-1);
    }
    int find(int x)
    {
    	re int l=1,r=n,ret=n+1;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(a[mid]>=x) ret=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return ret;
    }
    void getk(int x)//求与x的每一位不同的数个数:要求sigma(ki)=n-1
    {
    	ll now=0;//前面相等的位都 >=now 
    	for(re int i=m-1;i>=0;--i)//求ki 
    	{
    		if(x>>i&1)//这一位不同:[ now,now+(1<<i) )
    		{
    			int r=find(now+(1<<i))-1;//右边第一个 
    			int l=find(now);//左边第一个 
    			k[i]=(r-l+1);
    			now+=(1<<i);
    		}
    		else//这一位不同:[ now+(1<<i),now+(1<<(i+1)) )
    		{
    			int r=find(now+(1<<(i+1)))-1;
    			int l=find(now+(1<<i));
    			k[i]=(r-l+1);
    		}
    	}
    }
    int main()
    {	
    	freopen("race.in","r",stdin);
    	freopen("race.out","w",stdout);
    	read(n);read(m);
    	for(int i=1;i<=n;++i) read(a[i]);
    	sort(a+1,a+n+1);
    	init();
    	for(re int i=1;i<=n;++i)
    	{
    		getk(a[i]);
    		ll pts=0;
    		for(re int j=0;j<m;++j) pts=(pts+k[j]*((po*k[j]%mod + sum*(n-1-k[j])%mod)%mod)%mod)%mod;
    		pts=(pts%mod+mod)%mod;
    		ans^=pts;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    c++中,map的使用
    关于C++中vector<vector<int> >的使用
    python中如何向shell脚本传递带空格的参数
    数组初始化及赋值的方法,memset的使用
    leetcode:首个缺少的最小正整数(js实现。)
    安装cmake过程出错:Error when bootstrapping CMake: Cannot find a C++ compiler that supports both C++11 and the specified C++ flags.
    React系列--三大属性 props refs state
    React系列--组件Component
    React系列--jsx语法及虚拟dom,渲染
    c++的复制构造函数,(郑莉 c++语言程序设计)
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11776920.html
Copyright © 2020-2023  润新知