hdu 5898 - odd-even number (2016沈阳网络赛) - 数位dp
2016-09-21 15:29
579 查看
题意:如果一个数字连续的奇数都为偶数个,连续的偶数个数都为奇数,那么这个数字就是奇偶数,输入l,r,问[l, r]闭区间有多少个奇偶数。
数位dp用dp[i][j][k]表示长度为i并且最高位为j的数字并且与最高位数字奇偶情况相同的连续位数为k的满足题目条件的数字有多少个,k=0表示偶数长度,k=1表示奇数长度。状态转移的条件会比较麻烦:
如果高位为奇数位偶数,可以向偶数位偶数和奇数位奇数转移(就是可以在最高位前面添加以为奇数或者以为偶数);
如果高位为偶数位偶数,那么只能在前面添加一位偶数,即只能向奇数位偶数转移;
如果高位有偶数位奇数,可以向奇数位奇数转移也可以向奇数位偶数转移;
如果高位为奇数位奇数,只能向偶数位奇数转移。
查询和一般的数位dp差不太多,要注意高位的数字对当前状态的影响。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][10][2];
ll pw[20];
int judge(int l, int m, int j, int k) {
if(!l && !m && !j && k) return 1;
if(!l && m && !j && !k) return 1;
if((l ^ m) && j && k) return 1;
if(l && !m && !j && k) return 1;
if(l && m && j && !k) return 1;
return 0;
}
void init() {
dp[0][1][0] = 1;
int i, j, k, s, l, m;
for(i = 1; i < 20; i++) {
for(j = 0; j < 10; j++) {
for(k = 0; k < 2; k++) {
for(l = 0; l < 10; l++) {
for(m = 0; m < 2; m++) {
if(judge(l & 1, m, j & 1, k)) {
dp[i][j][k] += dp[i - 1][l][m];
}
}
}
}
}
}
pw[0] = 1;
for(i = 1; i < 19; i++) {
pw[i] = pw[i - 1] * 10;
}
}
ll add(ll u, int l, int o = 0, int n = 0, int f = 0) {
if(!u) return 0;
ll ans = 0;
ll t = pw[l - 1] * (u / pw[l - 1]);
for(int i = (f ? l : 1); i <= l; i++) {
for(int j = (f ? 0 : 1); j < 10 && j * pw[i - 1] < t; j++) {
if(!f || (o && !n)) ans += dp[i][j][(j & 1) ^ 1];
else if(o == 0 && n == 1) {
ans += dp[i][j][0];
}
else if(o && n && (j & 1)) {
ans += dp[i][j][1];
}
else if(!o && !n && (j & 1) == 0){
ans += dp[i][j][1];
}
}
}
int x = (u / pw[l - 1]) & 1;
if(x == o) ans += add(u % pw[l - 1], l - 1, x, n ^ 1,f + 1);
else if((o ^ n) || !f) ans += add(u % pw[l - 1], l - 1, x, 1,f + 1);
return ans;
}
int main() {
init();
ll d;
int t, ks = 1;
ll l, r;
scanf("%d", &t);
while(t--) {
scanf("%I64d%I64d", &l, &r);
int lt1 = 0, lt2;
r++;
for(lt1 = 18; pw[lt1] > l; lt1--);
for(lt2 = 18; pw[lt2] > r; lt2--);
printf("Case #%d: %I64d\n", ks++, add(r, lt2 + 1) - add(l, lt1 + 1));
}
return 0;
}
数位dp用dp[i][j][k]表示长度为i并且最高位为j的数字并且与最高位数字奇偶情况相同的连续位数为k的满足题目条件的数字有多少个,k=0表示偶数长度,k=1表示奇数长度。状态转移的条件会比较麻烦:
如果高位为奇数位偶数,可以向偶数位偶数和奇数位奇数转移(就是可以在最高位前面添加以为奇数或者以为偶数);
如果高位为偶数位偶数,那么只能在前面添加一位偶数,即只能向奇数位偶数转移;
如果高位有偶数位奇数,可以向奇数位奇数转移也可以向奇数位偶数转移;
如果高位为奇数位奇数,只能向偶数位奇数转移。
查询和一般的数位dp差不太多,要注意高位的数字对当前状态的影响。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20][10][2];
ll pw[20];
int judge(int l, int m, int j, int k) {
if(!l && !m && !j && k) return 1;
if(!l && m && !j && !k) return 1;
if((l ^ m) && j && k) return 1;
if(l && !m && !j && k) return 1;
if(l && m && j && !k) return 1;
return 0;
}
void init() {
dp[0][1][0] = 1;
int i, j, k, s, l, m;
for(i = 1; i < 20; i++) {
for(j = 0; j < 10; j++) {
for(k = 0; k < 2; k++) {
for(l = 0; l < 10; l++) {
for(m = 0; m < 2; m++) {
if(judge(l & 1, m, j & 1, k)) {
dp[i][j][k] += dp[i - 1][l][m];
}
}
}
}
}
}
pw[0] = 1;
for(i = 1; i < 19; i++) {
pw[i] = pw[i - 1] * 10;
}
}
ll add(ll u, int l, int o = 0, int n = 0, int f = 0) {
if(!u) return 0;
ll ans = 0;
ll t = pw[l - 1] * (u / pw[l - 1]);
for(int i = (f ? l : 1); i <= l; i++) {
for(int j = (f ? 0 : 1); j < 10 && j * pw[i - 1] < t; j++) {
if(!f || (o && !n)) ans += dp[i][j][(j & 1) ^ 1];
else if(o == 0 && n == 1) {
ans += dp[i][j][0];
}
else if(o && n && (j & 1)) {
ans += dp[i][j][1];
}
else if(!o && !n && (j & 1) == 0){
ans += dp[i][j][1];
}
}
}
int x = (u / pw[l - 1]) & 1;
if(x == o) ans += add(u % pw[l - 1], l - 1, x, n ^ 1,f + 1);
else if((o ^ n) || !f) ans += add(u % pw[l - 1], l - 1, x, 1,f + 1);
return ans;
}
int main() {
init();
ll d;
int t, ks = 1;
ll l, r;
scanf("%d", &t);
while(t--) {
scanf("%I64d%I64d", &l, &r);
int lt1 = 0, lt2;
r++;
for(lt1 = 18; pw[lt1] > l; lt1--);
for(lt2 = 18; pw[lt2] > r; lt2--);
printf("Case #%d: %I64d\n", ks++, add(r, lt2 + 1) - add(l, lt1 + 1));
}
return 0;
}
相关文章推荐
- HDU 5898 odd-even number (数位DP) -2016 ICPC沈阳赛区网络赛
- 【2016-沈阳赛区网络赛-G】记忆化搜索,数位DP(odd-even number,hdu 5898)
- hdu 5898 odd-even number (数位dp 沈阳网络赛)
- HDU 5898 odd-even number 2016年沈阳网络赛 (数位dp)
- hdu 5898 odd even number(acm/icpc沈阳赛区网络赛,数位DP)
- hdu 5898 odd-even number 2016ACM/ICPC沈阳赛区网络赛1007
- HDU 5898 odd-even number (数位DP) 2016 ACM/ICPC Asia Regional Shenyang Online
- HDU 5898 odd-even number (数位DP) 2016 ACM/ICPC Asia Regional Shenyang Online
- hdu_5898_odd-even number(数位DP)
- HDU 5898 odd-even number (数位DP)
- hdu 5898 odd-even number (数位dp)
- hdu 5898 odd-even number(基础数位DP)
- hdu 5898 odd-even number (数位dp)
- HDU 5898 odd-even number 数位DP
- [hdu 5898 odd-even number] 数位DP
- hdu 5898 odd-even number 数位dp
- HDU 5898 odd-even number(数位DP)
- HDU 5898 odd-even number (数位DP)
- HDU 5898 odd-even number——数位dp
- HDU 5898 odd-even number (2016亚洲区沈阳站网络赛 )