hdu 4901 The Romantic Hero(计数dp)2014多校训练第4场1005

本文介绍了一种使用动态规划(DP)的方法来解决一个特定的问题:构造两个序列,使第一个序列中所有元素的异或值等于第二个序列中所有元素的AND值,并求出所有可能的构造方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给出n个数,构造两个序列,使得第一个序列里面所有元素的异或值等于第二个序列里面所有元素的AND(&)值,并且第一个序列里所有元素的下标都小于第二个序列里所有元素的下标。求一共有多少种构造方法,结果对1000000007取余。

虽然比赛时就知道是dp,但是由于dp功底太弱,导致比赛时没有做出来。
分析:
dp1[i][j]:由0~i的元素异或得到j的种类数。
dp2[i][j]:由i~n-1的元素AND得到j的种类数。
dp3[i][j]:由i~n-1的元素,且一定包含a[i],AND得到j的种类数。
求出这些,最后把dp1[i][j]*dp3[i+1][j]求和就得到答案了!

这里多用了一个数组dp3,而不是直接用dp2,是为了防止重复计数。


代码:

#include<cstdio>
#include<cstring>

typedef __int64 LL;
#define mod 1000000007
const int MAXN = 1002;
const int MAXA = 1025;
int dp1[MAXN][MAXA], dp2[MAXN][MAXA], dp3[MAXN][MAXA];
int a[MAXN];

int main()
{
    int T, n, i, j, t;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(i = 0; i < n; i++)
            scanf("%d",&a[i]);
        memset(dp1, 0, sizeof(dp1));
        memset(dp2, 0, sizeof(dp2));
        memset(dp3, 0, sizeof(dp3));
        dp1[0][a[0]] = 1;
        for(i = 1; i < n - 1; i++) {
            dp1[i][a[i]]++; //单独一个元素构成一个集合
            for(j = 0; j < MAXA; j++) {
                if(dp1[i-1][j]) {
                    dp1[i][j] += dp1[i-1][j]; //不添加第i个元素进行异或,继承之前算好的
                    dp1[i][j] %= mod;

                    t = j ^ a[i];  //添加第i个元素进行异或
                    dp1[i][t] += dp1[i-1][j];
                    dp1[i][t] %= mod;
                }
            }
        }
        dp2[n-1][a[n-1]] = 1;
        dp3[n-1][a[n-1]] = 1;
        for(i = n-2; i > 0; i--) {
            dp2[i][a[i]]++;
            dp3[i][a[i]]++;   //单独一个元素构成一个集合
            for(j = 0; j < MAXA; j++) {
                if(dp2[i+1][j]) {
                    dp2[i][j] += dp2[i+1][j];  //不添加第i个元素进行按位与
                    dp2[i][j] %= mod;

                    t = j & a[i]; //添加第i个元素进行按位与
                    dp2[i][t] += dp2[i+1][j];
                    dp2[i][t] %= mod;

                    dp3[i][t] += dp2[i+1][j]; //添加第i个元素进行按位与
                    dp3[i][t] %= mod;
                }
            }
        }
        int ans = 0;
        for(i = 0; i < n - 1; i++) {
            for(j = 0; j < MAXA; j++) {
                if(dp1[i][j] && dp3[i+1][j]) {
                    ans += (LL(dp1[i][j]) * dp3[i+1][j] % mod);
                    ans %= mod;
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值