题意:有n个数(n<=1000),每个数<1024,现在给出a、b两个集合,需要你从这n个数中取出一些数,将取出的数放在a集合或者b集合,a集合的数的最大下标严格小于b集合所取的数的最小下标,然后a集合的数进行抑或操作,b集合的数进行与操作,问你操作后,这两个数相等,问你有多少种不同的方法。
思路:我发现暑假集训开始到现在,我一直在酱油中,状态根本没回升--以前遇到这样的题目,果断是可以出的,而在比赛的时候,还是队友提示,我才想到思路的。
其实,我们可以开个二维数组来记录在第i个数放入a集合时所取到的数的种数......与就是dp[1005][1024]
这样,不仅记录了在a集合取第i个数的时候,这个数是否存在,还记录了这个数存在的情况下,出现的次数。。。。。。
然后,我还开了个数组,记录b集合的。
这里需要注意,要将b集合的数据,全部集中到一起,而a集合的数据则不必,只需要再开一个数组,将a集合前面所有出现过的数以及次数保存下来,在去第i个数时,不仅
要判断i-1的情况,还要判断i-2,i-3,i-4.....1的所有情况,但是不能像b集合那样操作,将所有数据都集中在一起,那样会多的。
只能是再开一个数组,记录a集合i-1以前所有出现过的数据。
ac代码:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef __int64 ss; const ss mod=1000000007; int dp[1005][1080],t[1005][1080],n,s[1005],maxn; int p[1005][1080]; /* dp数组是用来记录a集合中与前i-1个数第i个数异或的结果以及次数 p数组,用来记录a集合中i-1之前出现的数的次数 t数组,用来记录b集合中i到n之间所有出现的结果以及次数,不与i异或,但 也出现了的结果,要是记录在t数组中 */ int main() { int text; scanf("%d",&text); while(text--) { memset(dp,0,sizeof(dp)); memset(t,0,sizeof(t)); memset(p,0,sizeof(p)); scanf("%d",&n); maxn=0; for(int i=1;i<=n;i++) { scanf("%d",&s[i]); maxn|=s[i]; } dp[1][s[1]]=1; t[n][s[n]]=1; p[1][s[1]]=1; for(int i=2;i<n;i++) { dp[i][s[i]]++; p[i][s[i]]++; p[i][s[i]]%=mod; dp[i][s[i]]%=mod; for(int j=0;j<=maxn;j++) { p[i][j]+=p[i-1][j]; p[i][j]%=mod; if(dp[i-1][j]) { int tmp=j^s[i]; dp[i][tmp]+=dp[i-1][j]; p[i][tmp]+=dp[i-1][j]; p[i][tmp]%=mod; dp[i][tmp]%=mod; } if(i-2>0) { if(p[i-2][j]) { ss tmp=j^s[i]; dp[i][tmp]+=p[i-2][j]; p[i][tmp]+=p[i-2][j]; p[i][tmp]%=mod; dp[i][tmp]%=mod; } } } } for(int i=n-1;i>1;i--) { t[i][s[i]]++; t[i][s[i]]%=mod; for(int j=0;j<=maxn;j++) { t[i][j]+=t[i+1][j]; t[i][j]%=mod; if(t[i+1][j]) { ss tmp=s[i]&j; t[i][tmp]+=t[i+1][j]; t[i][tmp]%=mod; } } } ss ans=0; for(int i=1;i<n;i++) { for(int j=0;j<=maxn;j++) { ans+=(((__int64)dp[i][j]*(__int64)t[i+1][j])%mod); ans%=mod; } } printf("%I64d\n",ans); } return 0; }
hdu4901
时间: 2024-10-22 11:13:35