SHUOJ 1771 - 奇偶和(数位DP)

http://202.121.199.212/JudgeOnline/problem.php?id=1771

夏季赛H题

Description

Input

第一行含有一个正整数 T,表示有 T 组测试数据。
每组数据只有一行,包含三个整数 L_i,R_i,m。
约定
    T≤200;
    0≤L≤R≤10^18;
    |m|≤100。

Output

对于每组测试用例,输出:
第一行:Case #: (# 要替换成对应的数字)。
输出两个整数,用一个空格分割。分别为在 [L_i,R_i ] 区间,有多少个数奇偶和等于 m,以及这些数的和(对和取模100000007后输出)。

Sample Input

3
1 10 2
10 20 4
10 30 -1

Sample Output

Case 1:
1 2
Case 2:
0 0
Case 3:
2 33

HINT

[1,10] 之间奇偶和为2的是2;

[10,20] 之间没有奇偶和为4的;

[10,30] 之间奇偶和为 -1的有10 23。

解题报告:

邝神出的题,果然不好做..orz

这道题用数位DP来做, 算是第一次写..比赛时挂了10次..还算是最后过了

奇偶和的概念很简单, 就是所有数位上偶数的和减去奇数的和

思路如下:

首先将0~9, 0~99, 0~999, ...奇偶和为m的利用数位DP写出一个计算函数

然后对于一个任意数n 将它分割为 1~9..9 等

如 123 可以分为 1~99, 100~109, 110~119, 120, 121, 122, 123

然后对各部分使用数位dp计算结果 其中数字个数比较好处理, 而数字的和比较难处理, 注意算数字和的时候要先求模(因为这个挂了N次)

分解的计算个数为最多 18*10 次, 并不是一个大数字

数位dp的状态也最大只有 18*(18*9) 种

最后出答案的时候记住两个相减求模的时候会出现负数 要再+MOD 再求模

分解的部分(solve函数)在比赛的时候写麻烦了, 后来参照了邝神的标程, 改成了简单一些的代码.

代码如下

#include <iostream>
#include <sstream>
#include <ios>
#include <iomanip>
#include <functional>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <climits>
#include <cctype>
using namespace std;
#define INF 0x3FFFFFFF
#define MP(X,Y) make_pair(X,Y)
#define PB(X) push_back(X)
#define REP(X,N) for(int X=0;X<N;X++)
#define REP2(X,L,R) for(int X=L;X<=R;X++)
#define DEP(X,R,L) for(int X=R;X>=L;X--)
#define CLR(A,X) memset(A,X,sizeof(A));
#define IT iterator
typedef long long ll;
typedef pair<ll,ll> PII;
typedef vector<PII> VII;
typedef vector<int> VI;
#define X first
#define Y second

#define MOD 100000007

int fsum(ll n)
{
    ll r=0;
    do{
        ll t=n%10;
        r-=t*((t&1)*2-1);
        n/=10;
    }while(n);
    return r;
}

ll dp[20][2000];
ll sum[20][2000];
ll ten[19];

PII dfs(int n, int m)
{
    if(n<=0)
    {
        if(m==0) return MP(1,0);
        return MP(0,0);
    }
    ll &dd = dp[n][m+1000];
    ll &ds = sum[n][m+1000];
    if(dd!=-1) return MP(dd,ds);
    ll count=0, sum=0;
    REP2(i,0,9)
    {
        PII r=dfs(n-1,m-fsum(i));
        count+=r.X;
        sum+=((i*(ten[n]%MOD)%MOD*(r.X%MOD))%MOD+r.Y);
        sum%=MOD;
    }
    dd=count;
    ds=sum;
    return MP(count, sum);
}

PII solve(ll n, ll m)
{
    if(n<0) return MP(0,0);
    ll count=0, sum=0;
    int x=0;
    n++;
	do{
		REP(i,n%10)
		{
    		PII r=dfs(x, m-fsum(n-i-1));
    		count+=r.X;
			sum+=(((n-i-1)*ten[x+1])%MOD)*(r.X%MOD)%MOD+r.Y;
			sum%=MOD;
		}
    	n/=10;
    	x++;
    }while(n);
    return MP(count, sum);
}    

int main()
{
    ios::sync_with_stdio(false);
    REP(i,19)
    {
        ten[i]=i>1?ten[i-1]*10:1;
        ten[i]%=MOD;
    }
    ten[0]=0;
    CLR(dp,-1);
    int t,cs=1;
    ll l,r,m;
    cin>>t;
    while(t--)
    {
        cout<<"Case "<<cs++<<":"<<endl;
        cin>>l>>r>>m;
        PII a=solve(l-1,m);
        PII b=solve(r,m);
        cout<<b.X-a.X<<‘ ‘<<((b.Y-a.Y)%MOD+MOD)%MOD<<endl;
    }
    return 0;
}

SHUOJ 1771 - 奇偶和(数位DP)

时间: 2024-10-28 17:11:33

SHUOJ 1771 - 奇偶和(数位DP)的相关文章

hdu 5898 odd-even number 数位DP

odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 716    Accepted Submission(s): 385 Problem Description For a number,if the length of continuous odd digits is even and the length

bin巨的数(数位DP)

题目描述 作为ACM史上年度重量级人物,bin巨目前已经掌握了史上最NB的数,群巨快来仰慕!!我们定义这样一个数,它里面的每一个数字都是成双成对出现 的,but,如果这个数里面存在0那么这也是NB的数,比如11,122122,12035,当然,需要剔除那些首位是0的数.我们的目标就是计算一个区 间内bin巨有多少NB数! 输入 输入第一行包含一个整数T,表示接下来有T组数据. 下面T行,每行包含两个数l和r,表示这个区间. 数据范围:0<=l<=r<=10^18 输出 输出T行,每行一个

ACM之路(16)—— 数位DP

题目就是kuangbin的数位DP. 先讲C题,不要62,差不多就是一个模板题.要注意的是按位来的话,光一个pos是不够的,还需要一维来记录当前位置是什么数字,这样才能防止同一个pos不同数字的dp值混在一起.直接丢代码: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <iostream> 5 #include <vector> 6 #in

数位dp小练

最近刷题的同时还得填填坑,说来你们也不信,我还不会数位dp. 照例推几篇博客: 数位DP讲解 数位dp 的简单入门 这两篇博客讲的都很好,不过代码推荐记搜的形式,不仅易于理解,还短. 数位dp的式子一般是这样的:dp[i][][]表示到第\(i\)位,而后面几维就因题而异了. 不过通用的思想就是利用前缀相减求出区间信息. 算了上题吧. [SCOI2009]windy数 这都说是数位dp入门题. 根据这题,受到影响的数只有相邻两个,因此dp[i][j]表示到第\(i\)位(从高往低)上一位的数\(

数位dp HDU - 5898 odd-even number

http://acm.hdu.edu.cn/showproblem.php?pid=5898 题意:求两个数中间的满足连续位是奇数的长度是偶数,连续位上是偶数的长度位奇数的数量. 分析:就是数位dp基本写法,dfs 的参数多加了个 a 表示连续位数的长度,flag表示上一位是否为奇数.  这里当奇偶改变时,a 就要变成1 ,并且可以用 a=0 来表示区分是前导0 .然后 记忆化搜索的过程中 ,用continue 表示不满足条件,暂时满足才往下接着搜索. 然后!!     &运算的优先程度比 ==

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

51nod1043(数位dp)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1043 题意:中文题诶- 思路:数位dp 我们用dp[i][j]来存储长度为2*i且一半和为j的所有情况(包括前导0的情况),为了方便我们现在只讨论其一半的和的情况,因为如果包括前导0的话其两边的情况是一样的: 我们假设再长度为i-1的数字最前面加1位数字k,0<=k<=9(这位数字加在哪里并不影响答案,因为我们在计算i-1长度的时候已经计算了所有组合情况,

数位dp

1.[hdu3709]Balanced Number 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cstdlib> 6 #include<algorithm> 7 #include<ctime> 8 #include<cmath> 9 #include<queue>