• 清华集训2015-Day 1


    玛里苟斯

    一个大小为 (n) 的可重集合 (a) ,求 (mathbb E[x^k]) ,其中 (x)(a) 的一个子集的异或和。

    (nle 10^5,1le kle 5) ,保证答案小于 (2^{63})

    分析

    这题挺妙的呢。

    保证答案小于 (2^{63}) ,其实是告诉我们,答案的二进制位数 小于 64 位 。这就是说,对于一个 (k)(a_i) 的二进制位数 (m) 小于 (frac{64}{k})

    首先明确这样一件事情:若 (a) 中某一位有 1,那么在所有异或和中,这一位为 1 的占 (frac{1}{2}) 。也就是说,若这一位没有 1,那么所有异或和中这一位的期望为 0,否则为 (frac{1}{2})

    这是因为,对于其他元素的所有子集的异或和,异或上这一位与不异或这一位,必定有一个 1 一个 0 ,所以 1 一定占刚好 (frac{1}{2})

    (S)(n) 的所有子集的异或和的可重集合,对每一位分开考虑,那就有

    [egin{aligned} ext{ans}&=sum _{sin S}(sum_{i=0}^m 2^i[s_i=1])^k \ &=sum _{c_1=0}^msum _{c_2=0}^mcdotssum _{c_k=0}^mfrac{2^{sum c}}{2^n}sum _{sin S}[s_{c_p}=1,pin [1,k]] \ &=sum _{c_1=0}^msum _{c_2=0}^mcdotssum _{c_k=0}^m2^{sum c}P(a中c_i位上均为1) \ &=sum _{c_1=0}^msum _{c_2=0}^mcdotssum _{c_k=0}^m2^{sum c}frac{[a中c_i位上均有1]}{2^{c_i中不同的个数}} \ end{aligned} ]

    最后的形式可以对 (c) 中的位建线性基来求。总复杂度为 (O(m^{k+1}k)=O(64m^{k-1}))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long giant;
    inline char nchar() {
    	static const int bufl=1<<20;
    	static char buf[bufl],*a,*b;
    	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
    }
    template<class T> inline T read() {
    	T x=0;
    	char c=nchar();
    	for (;!isdigit(c);c=nchar());
    	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
    	return x;
    }
    const int maxn=1e5+1;
    const int maxj=64;
    const int maxk=5;
    template<typename T> inline void Max(T &x,T y) {x=max(x,y);}
    int b[maxk],c[maxk],n,m=0,k;
    giant ans=0,a[maxj],s[maxn];
    void dfs(int now) {
    	if (now==k) {
    		memset(b,0,sizeof b);
    		int gs=1;
    		for (int j=m;j>=0;--j) {
    			int x=0;
    			for (int i=k;i--;) if ((a[j]>>c[i])&1) x|=1<<i;
    			for (int i=k;i--;) if ((x>>i)&1) (b[i]?0:(b[i]=x,--gs)),x^=b[i];
    		}
    		int x=(1<<k)-1;
    		for (int i=k;i--;gs+=c[i]) if ((x^b[i])<x) x^=b[i];
    		if (!x) ans+=1ull<<gs;
    		return;
    	}
    	for (int i=0;i<=m;++i) c[now]=i,dfs(now+1);
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    #endif
    	n=read<int>(),k=read<int>();
    	for (int i=1,j;i<=n;++i) {
    		giant &x=s[i]=read<giant>();
    		for (j=maxj;j--;) if ((x>>j)&1) break;
    		Max(m,j);
    	}
    	for (int i=1;i<=n;++i) {
    		giant x=s[i];
    		for (int j=m;j>=0 && x;--j) if ((x>>j)&1) (a[j]?0:(a[j]=x)),x^=a[j];
    	}
    	dfs(0);
    	printf("%lld%s
    ",ans>>1,ans&1?".5":"");
    	return 0;
    }
    

    主旋律

    一个 (n) 个点 (m) 条边的有向图,问有多少种删边方案使得得到的有向图仍然是一个强连通图。(nle 15,mle n(n-1))

    分析

    问题转化为有多少个点集不变的子图是强连通的。

    复习一下强连通图计数,即可得到这题的方法——不过是在这个图上进行。

    (f(S)) 为仅包含点集 (S)强连通图 个数。

    (g(S)) 为仅包含点集 (S)多个(包括 1 个)强连通块组成的图个数,带容斥系数,奇数个连通块加,偶数个连通块减

    (h(S)) 为仅包含点集 (S) 的有向图个数。

    上述图的边都属于原图的边集。

    有以下两条关系式:

    [egin{aligned} h(S)&=sum _{emptyset subsetneqq Tsubset S} g(T)h(Ssetminus T)2^{b(T,Ssetminus T)} \ g(S)&=f(S)-sum _{emptyset subsetneqq Tsubsetneqq S} f(T)g(Ssetminus T) end{aligned} ]

    其中 (b(A,B)) 表示 (|{(u,v)|,uin A,vin B,(u,v)in E}|)

    这可以用 in[x][s],out[x]][s] 分别表示 (x) 到一个集合的边数和集合到 (x) 的边数预处理。

    预处理的部分会用到莫比乌斯变换,即子集之和。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long giant;
    inline int read() {
        int x=0,f=1;
        char c=getchar();
        for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
        for (;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=15;
    const int maxm=maxn*(maxn-1);
    const int maxs=1<<maxn;
    const int q=1e9+7;
    inline int Plus(int x,int y) {return (x+=y)>=q?x-q:x;}
    inline void Pe(int &x,int y) {x=Plus(x,y);}
    inline int Multi(int x,int y) {return 1ll*x*y%q;}
    inline void Me(int &x,int y) {x=Multi(x,y);}
    inline int Sub(int x,int y) {return (x-=y)<0?x+q:x;}
    inline void Se(int &x,int y) {x=Sub(x,y);}
    inline int mi(int x,int y) {
        int ret=1;
        for (;y;y>>=1,Me(x,x)) if (y&1) Me(ret,x);
        return ret;
    }
    int n,m,S,in[maxn][maxs],out[maxn][maxs],e[maxs],b[maxs],two[maxm+1];
    int f[maxs],g[maxs],h[maxs];
    struct bian {
        int x,y;
    } a[maxm];
    int main() {
    #ifndef ONLINE_JUDGE
        freopen("test.in","r",stdin);
    #endif
        two[0]=1;
        for (int i=1;i<=maxm;++i) two[i]=Multi(two[i-1],2);
        n=read(),m=read(),S=1<<n;
        for (int i=0;i<m;++i) {
            int &x=a[i].x=read()-1,&y=a[i].y=read()-1;
            ++in[y][1<<x],++out[x][1<<y],++e[(1<<x)|(1<<y)];
        }
        for (int i=0;i<n;++i) for (int j=0;j<n;++j) for (int s=0;s<S;++s) if ((s>>j)&1) Pe(in[i][s],in[i][s^(1<<j)]),Pe(out[i][s],out[i][s^(1<<j)]);
        for (int i=0;i<n;++i) for (int s=0;s<S;++s) if ((s>>i)&1) Pe(e[s],e[s^(1<<i)]);
        for (int s=1;s<S;++s) h[s]=two[e[s]];
        for (int s=1;s<S;++s) { // rig?
            for (int t=s;;t=(t-1)&s) {
                int &bt=b[t]=0;
                if (t==s) continue;
                if (!t) break;
                int o=s^t,x=__builtin_ctz(o);
                bt=Plus(Sub(b[t^(1<<x)],out[x][o^(1<<x)]),in[x][t]);
            }
            int &gs=g[s]=h[s];
            for (int t=s;t;t=(t-1)&s) Se(gs,Multi(Multi(g[t],h[s^t]),two[b[t]]));
        }
        for (int s=1;s<S;++s) {
            int v=__builtin_ctz(s),&fs=f[s]=g[s];
            for (int t=(s-1)&s;t;t=(t-1)&s) if ((t>>v)&1) Pe(fs,Multi(f[t],g[s^t]));
        }
        printf("%d
    ",f[S-1]);
        return 0;
    }
    

    奇数国

    简单题,线段树+压位。

  • 相关阅读:
    Java集合类: Set、List、Map、Queue使用场景梳理
    Java通过JDBC进行简单的增删改查(以MySQL为例)
    Servlet学习笔记
    Tomcat原理详解
    Tomcat的JVM和连接数设置
    Google Chrome 未响应。是否立即重新启动?---解决方法(秒速解决)
    LDAP服务器的概念和原理简单介绍
    Java IO流学习
    JavaScript_js模拟键盘输入
    Java_类似java.lang.VerifyError: Expecting a stackmap frame at branch target 22 in method的解决方法
  • 原文地址:https://www.cnblogs.com/owenyu/p/7624446.html
Copyright © 2020-2023  润新知