做题TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp
Posted cly-none
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了做题TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp相关的知识,希望对你有一定的参考价值。
原文链接https://www.cnblogs.com/cly-none
题意:求有多少对集合(S,T)满足:(S subseteq {1,2...n }, T subseteq {1,2...m},S igcap T = emptyset),且(S)中所有元素的异或和小于(T)中所有元素的异或和。对(10^9+7)取模。
(n,m leq 2000)
首先,通过记录当前两个集合的异或和,转移时考虑每个元素的3种选择,容易得到(O(n^3))的暴力dp。然而,要对此优化却是一件困难的事情。
但无论如何,对状态的优化的必要的。因此,我们就必须避免同时记录两个集合的异或和。考虑两个异或和如果只有一位,那么它们的大小关系就能通过记录一位来得到。而对于多位的二进制数的大小比较,我们也只用比较不同的最高位就可以了。
因此,我们枚举不同的最高位。那么,我们就可以忽略后面的位,并只用记录我们所枚举的这一位。剩下的问题就在于保证更高的位是相等的。那可以用记录两个数在更高位上的异或和实现,异或和为0,这两个数就是相等的。
时间复杂度(O(n^2log n))。
#include <bits/stdc++.h>
using namespace std;
const int N = 2060, MOD = (int)(1e9 + 7);
int dp[N][N][2],n,m,len,ans;
class WinterAndSnowmen {
public:
int getNumber( int N, int M );
};
int WinterAndSnowmen::getNumber(int N, int M) {
n = N, m = M;
len = max(n,m);
for (int s = 1 ; s <= 12 ; ++ s) {
memset(dp,0,sizeof dp);
dp[0][0][0] = 1;
for (register int i = 1 ; i <= len ; ++ i) {
for (register int j = 0 ; j < 2048 ; ++ j) {
for (int k = 0 ; k < 2 ; ++ k) {
if (i <= n)
(dp[i][j^(i>>(s-1))][k] += dp[i-1][j][k]) %= MOD;
if (i <= m)
(dp[i][j^(i>>(s-1))][k ^ ((i >> (s-1))&1)] += dp[i-1][j][k]) %= MOD;
(dp[i][j][k] += dp[i-1][j][k]) %= MOD;
}
}
}
(ans += dp[len][1][1]) %= MOD;
}
return ans;
}
小结:虽然是“思维训练”中的题,但也会有自己没有掌握的技巧。
以上是关于做题TCSRM601 Div1 500 WinterAndSnowmen——按位考虑&dp的主要内容,如果未能解决你的问题,请参考以下文章
特定字符串的正则表达式匹配,例如 Ord(500) Ord(601) Ord(537)
TopCoder SRM502 Div1 500 贪心 01背包