题意:
有n个数,从n个数中选出两个集合s和集合t,保证原序列中,集合s中的元素都在
集合t中元素的左边。且要求集合s中元素做抑或运算的值与集合t中元素做与运算的
值相等。问能选出多少种这样的集合s和t。
算法:
左右dp。
用dp[i][j]表示前i个数 做抑或运算得到j的方法数。最后一个值取不取到都不一定。
故为背包的问题。右边也是一样。
枚举时可能出现重复。枚举到第i个和枚举第i+1个可能重复。所以要枚举一个中间值。
这个中间值是归到s集的,因为抑或支持逆运算,而与是不支持的。
所以最后dp方程应该改为 前i个数一定包含第i个做抑或得到值j的方法数,即s[i][j]。
那么s[i][j] = dp[i-1][j^a[i]]。
右边也是做与运算,但要注意下标。
然后取long long应该在乘法之前,否则乘法运算后可能已经溢出,再做long long 就没意义了。
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int mod = 1000000000+7; typedef long long ll; int a[1010]; int dp[1010][1025],dp1[1010][1025],s[1010][1025]; int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(dp,0,sizeof(dp)); dp[0][0] = 1; for(int i=1;i<=n;i++) { for(int j=0;j<1024;j++) { dp[i][j] = dp[i-1][j]+dp[i-1][j^a[i]]; if(dp[i][j]>=mod) dp[i][j] -= mod; } for(int j=0;j<1024;j++) s[i][j] = dp[i-1][j^a[i]]; } memset(dp1,0,sizeof(dp1)); for(int i=n;i>=1;i--) { dp1[i][a[i]]++; for(int j=0;j<1024;j++) { dp1[i][j&a[i]] = (dp1[i][j&a[i]]+dp1[i+1][j])%mod; dp1[i][j] = (dp1[i][j]+dp1[i+1][j])%mod; } } int cnt = 0; for(int k=1;k<=n-1;k++) { for(int j=0;j<1024;j++) { cnt = (cnt+(ll)s[k][j]*dp1[k+1][j])%mod; if(cnt>=mod) cnt-=mod; } } printf("%d\n",cnt); } return 0; }
hdu 4901 The Romantic Hero (dp+背包问题)
时间: 2024-10-23 07:15:33