• BZOJ4036: [HAOI2015]按位或


    BZOJ4036: [HAOI2015]按位或

    https://lydsy.com/JudgeOnline/problem.php?id=4036

    分析:

    • (f_{i,s})表示(i)次后数字是(s)的概率。
    • 那么答案就是(sumlimits_{i=1}^{infty}i imes (f_{i,all}-f_{i-1,all}))
    • (=infty -sumlimits_{i=1}^{infty}f_{i,all})
    • 考虑逐项计算(f),可以发现每次计算是一个或卷积的形式。
    • 即如果设(g_{i,S}=sumlimits_{Tsubseteq S}f_{i,S}),那么(g_{i,S}=(P_S)^i)其中(P_S=sumlimits_{Tsubseteq S}p_s)
    • 于是上面的式子可以用(g)来改写,这里就懒得写了。
    • 求出(g)后,由于答案只需要(all)这一项, 这里选择用子集反演的方式直接求答案,能优化掉一个(fwt)

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1050000
    typedef double f2;
    int K,n,cnt[N];
    f2 p[N];
    void fwt(f2 *a,int len,int flg) {
    	int i,j,k,t;
    	for(k=2;k<=len;k<<=1)for(t=k>>1,i=0;i<len;i+=k)for(j=i;j<i+t;j++) {
    		if(flg==1) a[j+t]+=a[j]; else a[j+t]-=a[j];
    	}
    }
    int main() {
    	scanf("%d",&K);
    	n=(1<<K)-1;
    	int i,mk=0;
    	for(i=0;i<=n;i++) cnt[i]=cnt[i>>1]+(i&1),scanf("%lf",&p[i]),p[i]>1e-6?(mk|=i):0;
    	if(mk!=n) {puts("INF"); return 0;}
    	fwt(p,n+1,1);
    	f2 ans=0;
    	for(i=0;i<n;i++) {
    		if((K-cnt[i])&1) ans-=1/(1-p[i]);
    		else ans+=1/(1-p[i]);
    	}
    	printf("%.8f
    ",-ans);
    }
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    using namespace std;
    typedef long long ll;
    #define N 2100000
    #define mod 998244353
    ll f[22][N],g[22][N],A[N],sum[N];
    int ea[550],eb[550];
    int n,m,p,w[22],CNT[N];
    int head[22],to[550],nxt[550],cnt,Q[22],vis[22],du[22];
    inline void add(int u,int v) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    ll qp(ll x,ll y) {
    	ll re=1; for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod;
    	return re;
    }
    bool check(int s) {
    	int i;
    	for(i=1;i<=n;i++) du[i]=head[i]=vis[i]=0; cnt=0;
    	for(i=1;i<=m;i++) {
    		if((s&(1<<(ea[i]-1))) && (s&(1<<(eb[i]-1))))
    			add(ea[i],eb[i]),add(eb[i],ea[i]),du[ea[i]]++,du[eb[i]]++;
    	}
    	for(i=1;i<=n;i++) if(du[i]&1) return 0;
    	int l=0,r=0;
    	for(i=1;i<=n;i++) if(s&(1<<(i-1))) {
    		Q[r++]=i; vis[i]=1; break;
    	}
    	while(l<r) {
    		int x=Q[l++];
    		for(i=head[x];i;i=nxt[i]) if(!vis[to[i]]) {
    			vis[to[i]]=1; Q[r++]=to[i];
    		}
    	}
    	return r==CNT[s];
    }
    void fort(ll *a,int len,int flg) {
    	int i,j,k,t;
    	for(k=2;k<=len;k<<=1) for(t=k>>1,i=0;i<len;i+=k) for(j=i;j<i+t;j++) {
    		if(flg==1) a[j+t]=(a[j+t]+a[j])%mod; else a[j+t]=(a[j+t]-a[j])%mod;
    	}
    }
    int main() {
    	scanf("%d%d%d",&n,&m,&p);
    	int i,j,k;
    	for(i=1;i<=m;i++) scanf("%d%d",&ea[i],&eb[i]);
    	for(i=1;i<=n;i++) scanf("%d",&w[i]);
    	int mask=(1<<n)-1;
    	for(i=1;i<=mask;i++) {
    		CNT[i]=CNT[i>>1]+(i&1);
    		ll re=0;
    		for(j=1;j<=n;j++) if(i&(1<<(j-1))) {
    			re+=w[j];
    		}
    		g[CNT[i]][i]=qp(re,p); sum[i]=qp(g[CNT[i]][i],mod-2);
    		if(check(i)) g[CNT[i]][i]=0;
    	}
    	for(i=1;i<=n;i++) fort(g[i],1<<n,1);
    	for(i=0;i<=mask;i++) f[0][i]=1;
    	for(i=1;i<=n;i++) {
    		for(j=1;j<=i;j++) {
    			for(k=0;k<=mask;k++) {
    				A[k]=(A[k]+g[j][k]*f[i-j][k]);
    			}
    		}
    		for(k=0;k<=mask;k++) A[k]%=mod;
    		fort(A,1<<n,-1);
    		for(k=0;k<=mask;k++) {
    			f[i][k]=A[k]*sum[k]%mod; A[k]=0;
    		}
    		if(i<n) fort(f[i],1<<n,1);
    	}
    	printf("%lld
    ",(f[n][mask]+mod)%mod);
    }
    
  • 相关阅读:
    解决无线打印机休眠后掉线无法进行局域网打印的问题
    快速为某个目录的verilog文件生成filelist
    使用Visual Studio的Spy++查找弹窗广告进程
    【转载】verilog语法之generate语句的基本认识
    补码(为什么按位取反再加一):告诉你一个其实很简单的问题
    【转载】EDID的简介和解析
    win32diskimager 谨慎使用
    UXE的一些使用归纳
    如何在win8或win10系统里添加inf驱动程序
    STM32 USB HID
  • 原文地址:https://www.cnblogs.com/suika/p/10204659.html
Copyright © 2020-2023  润新知