• 小米网络赛 热身赛 J题 XOR


    https://ac.nowcoder.com/acm/contest/8409/J

    Pro:
    给定(n)个数字
    求所有异或和为(0)的子集的子集大小之和
    (n<=1e5)

    Sol:
    考虑一个弱化版本
    求所有异或和为(0)的子集个数
    考虑求一个线性基
    设其大小为m
    答案即为((2^{n-m}-1))
    证明的话考虑
    把答案分成两部分
    包含非基底元素的和不包含非基底元素的

    1.包含非基底元素
    性基中的元素可以随便选
    因为它们组合出的任意元素都一定可以被基底中的一组解唯一表示,从而异或和为0
    因此贡献为((2^{n-m}-1))

    2.不包含非基底元素
    有线性基的性质可知其=0一定只有平凡解
    故这部分的贡献为0

    在这个做法的基础上稍作改动就可以得到
    求所有异或和为(k)的子集个数的做法
    这里不做赘述

    再来考虑本题
    根据刚才的讨论
    我们很容易得到一个(n*64*64)的做法
    即对于每个元素分别算贡献
    也就是去计算其它n-1个元素的异或和为k的子集个数
    这个只需要维护一个前缀线性基和后缀线性基即可
    但显然这个题还需要一个更为优秀的做法

    仔细观察可以发现
    我们对这n个元素求一遍线性基后
    非基底元素的贡献是非常好计算的
    设线性基大小为m
    对于任意非基底元素,它的贡献显然是(2^{n-m-1})
    因为非基底元素中任意一个组合都可以被基底元素表示
    这样的话只需要再对非基底元素使用原方法进行计算
    总复杂度O(64^3+n*logn)

    #include<bits/stdc++.h>
    #define M 63
    #define N 110000
    #define db double
    #define ll long long
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const ll h=3,ki=149,mo=1e9+7;
    ll mod(ll x){return (x%mo+mo)%mo;}
    ll inc(ll x,ll k){x+=k;return x<mo?x:x-mo;}
    ll dec(ll x,ll k){x-=k;return x>=0?x:x+mo;}
    ll ksm(ll x,ll k)
    {
    	ll ans=1;
    	while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
    	return ans;
    }
    ll inv(ll x){return ksm(mod(x),mo-2);}
    ll read()
    {
    	char ch=0;ll x=0,flag=1;
    	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
    	return x*flag;
    }
    void write(ll x)
    {
    	if(!x)return (void)putchar(48);
    	if(x<0)putchar(45),x=-x;
    	ll len=0,p[20];
    	while(x)p[++len]=x%10,x/=10;
    	for(ll i=len;i>=1;i--)putchar(p[i]+48);
    }
    const db eps=1e-7,inf=1e9+7,pi=acos(-1);
    db Read(){db x;scanf("%lf",&x);return x;}
    void Write(db x){printf("%lf",x);}
    struct node
    {
    	ll size,f[M];
    	void clear(){size=0;for(ll i=62;i>=0;i--)f[i]=0;}
    	bool query(ll x)
    	{
    		for(ll i=62;i>=0;i--)if(1ll<<i&x){if(f[i])x^=f[i];else return false;}
    		return true;
    	}
    	bool insert(ll x)
    	{
    		for(ll i=62;i>=0;i--)if(1ll<<i&x){if(f[i])x^=f[i];else{size++,f[i]=x;return true;}}
    		return false;
    	}
    };
    node merge(const node &a,const node &b)
    {
    	node ans=a;
    	for(ll i=62;i>=0;i--)if(b.f[i])ans.insert(b.f[i]);
    	return ans;
    }
    node S,P;
    ll a[N],p[N];
    int main()	
    {
    	ll n;
    	while(~scanf("%lld",&n))
    	{
    		ll ans=0,cnt=0;
    		S.clear();P.clear();
    		for(ll i=1;i<=n;i++)
    		{
    			a[i]=read();
    			if(S.insert(a[i]))p[++cnt]=i;else P.insert(a[i]);
    		}
    		if(S.size<n)ans=1ll*(n-S.size)*ksm(2,n-S.size-1)%mo;
    		for(ll i=1;i<=cnt;i++)
    		{
    			node t=P;
    			for(ll j=1;j<=cnt;j++)if(i!=j)t.insert(a[p[j]]);
    			if(t.query(a[p[i]]))ans=inc(ans,ksm(2,n-1-t.size));
    		}
    		write(ans);putchar('
    ');
    	}
    	return 0; 
    }
    
  • 相关阅读:
    高精度计算
    高精度除以低精度
    P1258 小车问题
    POJ 2352 stars (树状数组入门经典!!!)
    HDU 3635 Dragon Balls(超级经典的带权并查集!!!新手入门)
    HDU 3938 Portal (离线并查集,此题思路很强!!!,得到所谓的距离很巧妙)
    POJ 1703 Find them, Catch them(确定元素归属集合的并查集)
    HDU Virtual Friends(超级经典的带权并查集)
    HDU 3047 Zjnu Stadium(带权并查集,难想到)
    HDU 3038 How Many Answers Are Wrong(带权并查集,真的很难想到是个并查集!!!)
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/13961885.html
Copyright © 2020-2023  润新知