HDU 4901 DP

来源:互联网 发布:月薪5万的程序员 编辑:程序博客网 时间:2024/05/23 02:00

我觉得这个DP挺难的。。。然而这只是lydrainbowcat学长幻灯片上的第一题……
明天考试要GG。
题意:
给你一个序列,让你选出两个集合S和T。保证S里的数都在T里的数的左边。求一共有多少个集合满足S的异或所得等于T的与的所得,并mod一个大素数。
思路:
上课讲的DP,可惜我写了一晚上。。。。。。
首先我们可以枚举断点。i
这样就把序列分成了两部分。
前半部分是集合S
可以进行DP
j表示前i个数xor得j的方案数
f[i][j]=(f[i-1][j]+f[i-1][j*a[i]])%mod
(上一个状态和a[i]进行异或操作)
后半部分是集合T
也可以进行DP
j表示进行与操作得j的方案数
d[i][j]=d[i+1][j]
(显然,如果上一个状态可以到j,这个状态也可以)
d[i][j]=d[i+1][j],d[i][j&a[i]]=(d[i+1][j]+d[i][j&a[i]])%mod;
(上一个状态和a[i]进行位与操作)
所以 ans=((long long)f[i-1][j^a[i]]*d[i+1][j]+ans)%mod;
(要有强制类型转换)

// by SiriusRen#include <bits/stdc++.h>#define F for(int j=0;j<1024;j++)using namespace std;int cases,n,a[1001],f[1001][1025],d[1001][1025],mod=1e9+7,ans;int main(){    scanf("%d",&cases);    while(cases--){        memset(f,0,sizeof(f)),memset(d,0,sizeof(d)),ans=0;        scanf("%d",&n);        for(int i=1;i<=n;i++)scanf("%d",&a[i]),d[i][a[i]]++;        f[0][0]=1;        for(int i=1;i<=n;i++)            for(int j=0;j<1024;j++)f[i][j]=(f[i-1][j^a[i]]+f[i-1][j])%mod;        for(int i=n-1;i>=1;i--){            F d[i][j]=d[i+1][j],d[i][j&a[i]]=(d[i+1][j]+d[i][j&a[i]])%mod;            d[i][a[i]]++;            F ans=((long long)f[i-1][j^a[i]]*d[i+1][j]+ans)%mod;        }        printf("%d\n",ans);    }}

一不小心还刷了Code Length的第一。
这里写图片描述

0 0
原创粉丝点击