其他分享
首页 > 其他分享> > 题解 SP2829 【TLE - Time Limit Exceeded】

题解 SP2829 【TLE - Time Limit Exceeded】

作者:互联网

题目链接

Solution TLE - Time Limit Exceeded

题目大意:给定 \(n,m\),和长为 \(n\) 的序列 \(c\),让你构造一个长为 \(n\) 的序列 \(a\),满足 \(a[i]\in [0,2^m)\) 且 \(c[i] \nmid a[i]\),\(\forall i \in [0,n),a[i] \;and\;a[i+1]=0\)

高维前缀和


分析:很容易列出一个 \(dp\) 式子

设 \(f(i,S)\) 表示序列前 \(i\) 项,末尾为 \(S\) 的方案数

那么 \(f(i,S)=\sum f(i-1,S')\),其中 \(S' \in [0,2^m)\) 满足 \(S\;and\;S'=0\)

由于是子集求和,相比直接枚举子集,高维前缀和是一个更好的方法

在二维前缀和中,有基于容斥原理的写法

for(int i = 1;i <= n;i++)
	for(int j = 1;j <= m;j++)
    	f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + v[i][j];

这样在低维可行,在高维复杂度将会呈指数级增长

for(int i = 1;i <= n;i++)
	for(int j = 1;j <= m;j++)
    	v[i][j] += v[i - 1][j];
for(int i = 1;i <= n;i++)
	for(int j = 1;j <= m;j++)
    	v[i][j] += v[i][j - 1];

子集求和的高维前缀和,本质上就是第二种写法的位运算优化

for(int d = 0;d < m;d++)//枚举维度
	for(int s = 0;s < (1 << m);s++)
    	if(s & (1 << d))v[s] += v[s ^ (1 << d)];

于是我们可以优化到 \(O(nm2^m)\)

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 64,maxm = 1 << 17,mod = 1e9;
inline int add(int a,int b){return (a + b) % mod;}
inline int mul(int a,int b){return (1ll * a * b) % mod;}
int f[maxn][maxm],n,m,c[maxn],t;
inline void solve(){
    scanf("%d %d",&n,&m);
    for(int i = 1;i <= n;i++)scanf("%d",&c[i]);
    memset(f,0,sizeof(f));
    f[0][0] = 1;
    for(int d = 0;d < m;d++)
        for(int s = 0;s < (1 << m);s++)
            if(s & (1 << d))f[0][s] = add(f[0][s],f[0][s ^ (1 << d)]);
    for(int i = 1;i <= n;i++){
        for(int s = 0;s < (1 << m);s++)
            if(s % c[i])f[i][s] = f[i - 1][((1 << m) - 1) & (~s)];
        for(int d = 0;d < m;d++)
            for(int s = 0;s < (1 << m);s++)
                if(s & (1 << d))f[i][s] = add(f[i][s],f[i][s ^ (1 << d)]);
    }
    printf("%d\n",f[n][(1 << m) - 1]);
}
int main(){
    scanf("%d",&t);
    while(t--)solve();
    return 0;
}

标签:前缀,int,题解,SP2829,子集,Time,序列,高维,长为
来源: https://www.cnblogs.com/colazcy/p/13917236.html