题目链接:
https://ac.nowcoder.com/acm/problem/19327
题目大意:
给你一个t,t次询问,每一次询问给你一个L,R,然后[L,R]中满足包含007(可以不连续),不含前导0的数字个数,把t次询问的结果异或起来就是答案
分析:
这道题也相对不是很难,但是有一个细节需要注意,怎么样去设置dp数组,以及维数的含义非常重要,这样可以避免重复的初始化而节省时间不超时,
我们设置dp数组为dp[pos][num][pre][res],pos表示当前位,num表示以及组成了007中多少个数字,例如我已经有00了,那么num就是2,已经有007了,那么num就是3,pre表示当前位的上一位数字,res表示剩下多少位没有枚举
细节:有人会问,为什么要多一个维数res用来记录还剩下多少位没有枚举,因为这样可以不用每一次都初始化dp数组,只需要开始初始化一次就可以了,这样节省了很多时间,如果不设置res这一维的话,那么会TLE,不相信可以试一试,所以设置dp维数的思想尤为重要.
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20];
ll f[20][4][10][20];
/*pos表示当前位置,num表示取到在007的第几个数字了,pre表示当前位前一位的数字
first处理前导0,res表示后面还剩多少位没有枚举*/
ll dfs(int pos,int limit,int num,int pre,int first,int res)
{
if(!pos)
{
if(num==3) return 1;
else return 0;
}
if(!limit&&~f[pos][num][pre][res]) return f[pos][num][pre][res];
int ed = a[pos];
if(!limit) ed = 9;
ll ans = 0;
for(int i=0;i<=ed;i++)
{
if(first&&!i)
{
ans+=dfs(pos-1,limit&&(i==ed),num,i,1,res-1);
continue;
}
if(num==3)
ans+=dfs(pos-1,limit&&(i==ed),3,i,0,res-1);
else if((!num&&!i)||((num==1)&&!i)||((num==2)&&(i==7)))
ans+=dfs(pos-1,limit&&(i==ed),num+1,i,0,res-1);
else
ans+=dfs(pos-1,limit&&(i==ed),num,i,0,res-1);
}
if(!limit) f[pos][num][pre][res] = ans;
return ans;
}
ll solve(ll n)
{
if(n<1007) return 0;
memset(a,0,sizeof(a));
int cnt = 0;
while(n)
{
a[++cnt] = n%10;
n/=10;
}
ll ans = dfs(cnt,1,0,0,1,cnt-1);
return ans;
}
int main()
{
memset(f,-1,sizeof(f));
int t;
cin>>t;
ll ans = 0;
while(t--)
{
ll l,r;
cin>>l>>r;
ll temp = solve(r)-solve(l-1);
ans^=temp;
}
cout<<ans<<'\n';
return 0;
}