P7802 [COCI2015-2016#6] SAN 题解
P7802 [COCI2015-2016#6] SAN 题解
知识点
数位 DP。
题意分析
定义一个函数 \(rev(i)\) 为 \(i\) 在十进制下翻转后得到的新数字。
给定一张表,其中元素的值由以下方式得到为:
有 \(Q\) 个询问:给定 \(L,R\),问整张表中有多少个数的大小在 \([L,R]\) 中。
思路分析
部分分
注意到当一个数 \(i\) 变成 \(i+rev(i)\) 后,该行在 \(i\) 后的数就等同于第 \(i+rev(i)\) 行的数,那么我们可以考虑做一个累加(你喜欢的话叫他线性 DP 也没事),定义 \(f_i\) 为 \(i\) 出现的次数,转移式:
最后再做一个前缀和即可。
正解
我们很容易就能发现:能够被表示成 \(i+rev(i),i \in N_{+}\) 的数并不在多数,也就是说,按照上面那一条转移式,\(f_i\) 大于 \(1\) 的并不多,我们可以把这些大于 \(1\) 的 \(f_i\) 通过数位 DP 求出来,也是前缀和处理一下,然后二分查询即可。
我们来考虑一下它的复杂度。假设我们现在枚举的 \(i\) 在十进制下有 \(len \in [1,10]\) 位的,也就是 \(\lfloor \lg {i} + 1 \rfloor = len\)。我们设一个数 \(x\) 其从低到高的第 \(i\in[1,len]\) 位为 \(x_i\),则有 \(x = \sum_{i=1}^{len} x_i \cdot 10^{i-1}\)。
设 \(k=i+rev(i)\),在不进位的情况下 \(k\) 满足下面这个条件:
而对于 \(k\) 的任意一位 \(k_i\),它都是由 \(i\) 的某两位加和得到,故有结论:\(k_i \in [0,18],\forall i \in [1,len]\)。
那么 \(k\) 的可能数就只有 \(O(19^{\lfloor \frac{len}{2} \rfloor})\) 级别,总数大概在 \(O(19^5)\) 级别,实际上略多一点,为 \(2541196\) 个。
最后的复杂度就大概在:
其中 \(n = 2541196,w = 19\)。
CODE
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define FOR(i,a,b) for(int i=(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i=(a);i>=(int)(b);--i)
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~(i);(i)=(g)[(i)].nxt,(y)=(g)[(i)].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
constexpr int N=1e5+10;
constexpr ll lim=1e10;
int Q;
int C[2][19];
ll sum;
ll pw[11];
map<ll,ll> cnt;
ll Rev(ll x,ll y=0){
for(;x;x/=10)y=y*10+x%10;
return y;
}
void DP(int l,int r,ll num,bool lead,ll f){
if(num>lim)return;
if(l>r)return cnt[num]+=f,void();
if(l==r){
FOR(i,lead,9)cnt[num+(i*pw[l]<<1)]+=f;
return;
}
FOR(i,lead,18)DP(l+1,r-1,num+(pw[l]+pw[r])*i,0,f*C[lead][i]);
}
signed main(){
pw[0]=1;
FOR(i,0,9)FOR(j,0,9)C[1][i+j]+=(i>0),++C[0][i+j];
FOR(i,1,10)pw[i]=pw[i-1]*10,DP(0,i-1,0,1,1);
for(auto &x:cnt){
ll y=x.first+Rev(x.first);
if(y<=lim)cnt[y]+=x.second;
sum+=x.second,x.second=sum;
}
for(cin>>Q;Q;--Q){
ll l,r;
cin>>l>>r,cout<<(--cnt.upper_bound(r))->second-(--cnt.lower_bound(l))->second+r-l+1<<endl;
}
return 0;
}
(啊哈想要最劣解就学我狂用 STL 吧!)

浙公网安备 33010602011771号