zoukankan      html  css  js  c++  java
  • JZOJ6344. 【NOIP2019模拟2019.9.7】Huge Counting

    Description

    在这里插入图片描述
    T<=10,k<=9;l,r<=1e12

    Solution

    • 显然如果没有mod 2 的话,f(x1…xn),就是从(1…1)走到(x1…xn)方案数
    • 把所有的x全部减一,则
      f(x1,x2...,xn)=(xi)!xi!f(x1,x2...,xn)=frac {(sum xi)! }{prod xi!}
    • 接下来我们只用考虑上式是否有2这个因子,如果没有就是奇数,那么mod 2 就有1的贡献。
    • s!s!中2的因子的个数为s2isum frac {s} {2^i}
    • 所以上式f的2的因子个数就是(除法皆为下取整)
      (xi2kxi2k)sum (frac {sum xi} {2^k}-sum frac {xi}{2^k})
    • 显然对于任意一个k,括号里的都大于等于0。
    • 考虑整除2k就相当于在2进制下把后k位截掉。
    • 如果有两个x在k位上都为1,那么相加之后进位到k+1位,而这里的又被截掉了,所以就会对k+1位的结果有+1的贡献。这样的话就会有大于0个2的因子了。
    • 所以我们可以得出结论,如果要满足有贡献,这些二进制下每一位所有的x中最多只有一个1.
    • 设状态记录哪几位顶住了上限,状压DP一下。
    • 因为有区间,所以容斥一下就好了
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define maxn 10
    #define ll long long 
    #define mo 990804011
    #define Q(i) (1ll<<i)
    using namespace std;
    
    int T,n,i,j,k,opt[1<<maxn];
    ll l[maxn],r[maxn],x[maxn],f[2][1<<maxn-1];
    
    
    ll Doit(){
    	for(i=1;i<=n;i++) if (x[i]<0) return 0;
    	ll mx=0; 
    	for(i=1;i<=n;i++) mx=max(mx,x[i]);
    	int cnt=0; while (mx) mx>>=1,cnt++;
    	memset(f[cnt&1],0,sizeof(f[cnt&1]));
    	
    	f[cnt&1][Q(n)-1]=1;
    	for(k=cnt-1;k>=0;k--) {
    		int p=k&1,q=p^1;
    		memset(f[p],0,sizeof(f[p]));
    		for(int S=0;S<Q(n);S++) if (f[q][S]){
    			int T=0;
    			for(int i=1;i<=n;i++) if ((S&Q(i-1))&&!(x[i]&Q(k)))
    				T+=Q(i-1);
    			f[p][T]+=f[q][S];
    			for(int i=1;i<=n;i++) if ((x[i]&Q(k))||!(S&Q(i-1))){
    				if (S&Q(i-1)) f[p][T^Q(i-1)]+=f[q][S];
    				else f[p][T]+=f[q][S];
    			}
    		}
    		for(int S=0;S<Q(n);S++) if (f[p][S]>=mo)
    			f[p][S]%=mo;
    	}
    	ll sum=0;
    	for(int S=0;S<Q(n);S++) sum+=f[0][S];
    	return sum%mo;
    }
    
    int main(){
    	freopen("c.in","r",stdin);
    	freopen("c.out","w",stdout);
    	scanf("%d",&T);
    	opt[0]=1;
    	for(i=1;i<1<<maxn;i++) opt[i]=-opt[i-(i&-i)];
    	while (T--){
    		scanf("%d",&n);
    		for(i=1;i<=n;i++) scanf("%lld%lld",&l[i],&r[i]);
    		ll ans=0;
    		for(int RS=0;RS<(1<<n);RS++){
    			for(i=1;i<=n;i++) if (RS&Q(i-1)) x[i]=l[i]-2;
    				else x[i]=r[i]-1;
    			(ans+=opt[RS]*Doit()%mo+mo)%=mo;
    		}
    		printf("%lld
    ",ans);
    	}
    } 
    
  • 相关阅读:
    ProxySQL!像C罗一样的强大!
    zookeepr 临时节点
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    JavaScript 的闭包用于什么场景
    随笔
    随笔
    随笔
    随笔
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090969.html
Copyright © 2011-2022 走看看