• 孤独


    Sample Input

    2

    2 3 4

    1 2 3

    Sample Output

    31

    考虑容斥。答案就是{至少满足一个话题的方案数}-{至少满足两个话题的方案数}+{至少满足三个话题的方案数}...

    假设一个转态为S(第i位上为1表示满足这个话题),我们就要将所有合法的同学都记下来,设为tot个,则S的方案数就是tot^{k}

    容斥方法与计算方法都非常简单,但关键是如何对每个S求出tot。

    我们设满足S的人数为f[S],我们和显然有2^{n}cdot m的方法求出f[S],但肯定T飞。

    考虑一个优化一点的做法,对于每个a[i],我们先将f[a[i]]++,然后f[S]+=sum _{Tsubset S}f[T]。这其实是枚举子

    集的方法,时间复杂度大概是3^{n}的,还是不太优秀。

    我们发现,对于S枚举所有包含他的集合未免太浪费了,那么我们就枚举包含S的最小的集合,也就是只比S多了一位的集合。

    代码如下:

    for(int i=1;i<=n;i++) {
    	for(int s=(1<<n)-1;s>0;s--) {
    		if(s&(1<<i-1)) {
    			f[s^(1<<i-1)]+=f[s];
    		}
    	}
    }

    注意:这两个循环的顺序不能交换!!

    为什么呢?因为交换后会出现重复计算的问题。比如说我们在计算1101对1000的贡献时,如果我们先枚举集合,在枚举转移的位置,就会出现如下图的问题:

    1101的贡献就被计算了两次,原因是不同的转移顺序可能得到相同的状态,于是就算重了。

    而我们最外层枚举转移顺序就不会出现这样的问题了。

    完整代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<set>
    #include<map>
    #include<vector>
    #include<ctime>
    #define ll long long
    #define mod 1000000007
    #define M 100005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m,k;
    ll w[M],cnt[1<<21],f[1<<21],ans;
    ll g[1<<21];
    int len[1<<21];
    ll ksm(ll t,ll x) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%mod)
    		if(x&1) ans=ans*t%mod;
    	return ans;
    }
    
    int Count(int s) {
    	int ans=0;
    	for(;s;s-=s&(-s)) ans++;
    	return ans;
    }
    int main() {
    	Get();
    	n=Get(),m=Get(),k=Get();
    	for(int i=1;i<=m;i++) {
    		w[i]=Get();
    		cnt[w[i]]++;
    	}
    	for(int s=1;s<(1<<n);s++) len[s]=Count(s);
    	for(int i=1;i<(1<<n);i++) f[i]=cnt[i];
    	for(int i=1;i<=n;i++) {
    		for(int s=(1<<n)-1;s>0;s--) {
    			if(s&(1<<i-1)) {
    				f[s^(1<<i-1)]+=f[s];
    			}
    		}
    	}
    	for(int s=1;s<(1<<n);s++) {
    		if(len[s]&1) (ans+=ksm(f[s],k))%=mod;
    		else ans=(ans-ksm(f[s],k)+mod)%mod;
    	}
    	cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    Android theme应用主题实现
    CSS_复习
    复习_数字与字符串
    网络编程
    JAVA--多线程
    File类
    c#中new hisase关键字
    内存泄漏、程序崩溃的排除办法
    解决跨线程问题的几种办法
    c#接口
  • 原文地址:https://www.cnblogs.com/hchhch233/p/9735815.html
Copyright © 2020-2023  润新知