题意&思路:
题意:求区间[1,n] 内满足i < j, 并且f(i) > f(j) 的数对数目?
学习了一波数位dp的一般写法,很爽。。。。
首先把原数字转化为二进制数,然后考虑dp[pos][diff][f1][f2][f3] 表示对于状态下的答案:
Pos:当前处于第pos位
Diff:f(i) - f(j)的值
F1: i与n的大小关系,1表示小于,0表示相等
F2: j与n的大小关系,1表示小于,0表示相等
F3: i与j的大小关系,1表示i < j答案就是dfs(cnt - 1, 0, 0, 0, 0)
采用记忆化搜索的方式,所以访问的状态数等于 n * n * 8,不会超时,加上一点小小的剪枝,可以通过此题。
从高位往低位枚举,每次可以在i,j放的数字为0或1,同时需要考虑枚举的数不能超过n,因为题中涉及的是两个数之间的关系,所以自然要加入表示大小关系的状态,每次加上贡献就行了
代码:
#include <bits/stdc++.h>
#define PB push_back
#define FT first
#define SD second
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> P;
const int N = 1750,MOD = 7+1e9;
char ss[N];
int cnt, bits[N];
int yu;
struct bign
{
int len;
int num[N];
bign()
{
len = 0;
memset(num, 0, sizeof(num));
}
bign(const char* number)
{
*this = number;
}
void fix()
{
while(len && num[len-1] == 0) len--;
if(len == 0) num[len++] = 0;
}
void operator = (char* number)
{
len = strlen(number);
for(int i = 0;i < len;i ++)
num[i] = number[len - i - 1] - '0';
fix();
}
bign operator / (const int& b)
{
bign ans;
yu = 0;
for (int i = len - 1; i >= 0; i--) {
yu = yu * 10 + num[i];
ans.num[i] = yu / b;
yu %= b;
}
ans.len = len;
ans.fix();
return ans;
}
};
bign n;
int B, dp[N][N + N/2][2][2][2];
void update(int& x, int y)
{
x += y;
if(x >= MOD) x -= MOD;
}
int dfs(int pos, int diff, int f1, int f2, int f3)
{
if(diff < 0 && (-diff) > (cnt/2)) return 0;
int& now = dp[pos][diff + B][f1][f2][f3];
if(now != -1) return now;
now = 0;
if(pos < 1) return now = (f3 && diff > 0);
int x1 = 0, y1 = (f1 ? 1: bits[pos]);
int x2 = 0, y2 = (f2 ? 1: bits[pos]);
for(int i = x1;i <= y1;i ++)
{
for(int j = x2;j <= y2;j ++)
{
int newf3 = f3;
if(newf3 == 0)
{
if(i < j) newf3 = 1;
if(i > j) continue;
}
update(now, dfs(pos - 1, diff + i - j, f1|(i<y1), f2|(j<y2), newf3));
}
}
return now;
}
void init()
{
cnt = 1;
n = ss;
while(n.len != 1 || n.num[0] != 0)
{
n = (n / 2);
bits[cnt ++] = yu;
}
B = cnt/2 + 1;
}
int main()
{
scanf("%s", ss);
init();
memset(dp, -1, sizeof dp);
printf("%d\n", dfs(cnt - 1, 0, 0, 0, 0));
return 0;
}