題意&思路:
題意:求區間[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;
}