CF70C Lucky Tickets
题意描述:
设 \(rev(i)\) 表示把 \(i\) 的每一位翻转之后所得到的数。
如果 $a\times b = rev(a) \times rev(b) $ , 则称数对 \((a,b)\) 是合法的。
\(f(n,m)\) 表示:\(i\leq n,j\leq m\), 且数对 \((i,j)\) 是合法的数对的个数。
现在给你三个数 \(x_{max},y_{max},w\), 让你求一组 \((x,y)\) 满足:\(f(x,y)\geq w\) 且 \(x\leq x_{max},y\leq y_{max}\) ,\(x\times y\) 最小,无解则输出 \(-1\)。
数据范围: \(x_{max},y_{max}\leq 10^5, w\leq 10^7\) 。
solution:
\(\ map\) + 双指针。
首先我们考虑一个问题就是怎么求出 \(f(n,m)\) 。
我们把 \(a\times b = rev(a)\times rev(b)\) 变形为:\({a\over rev(a)} = {rev(b)\over b}\)。
然后可以先开个 \(map\) 存一下 \(a\over rev(a)\) 出现的次数, 然后在枚举一下 \(b\) ,那么 \(b\) 对 \(f(n,m)\) 的贡献就是 \(rev(b)\over b\) 的出现次数。
在能求出来 \(f(n,m)\) 之后,我们这道题就很好做了。
我们可以先扫一遍求出 \(f(x_{max},y_{max})\) ,无解的情况就是 \(f(x_{max},y_{max}) > w\)
至于有解的情况,我们维护两个指针 \(l,r\) 表示 当 \(x=l\) 时,符合条件的 \(y\) 最小为 \(r\) 。
同时维护一个 变量 \(cnt\), 表示当前 \(f(l,r)\) 的值。
不难发现,随着 \(l\) 的递增, \(r\) 一定是单调递减的。
所以我们可以拿双指针维护一下,同时再开两个 \(map\) 用来统计 \(a\over rev(a)\) 和 \(rev(b)\over b\) 出现的次数。
当 \(l,r\) 指针移动的时候, 用 \(map\) 就可以直接维护出指针移动之后 \(cnt\) 的值 (类似于莫队的指针移动)。
复杂度: \(O(nlogn)\)
code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
int maxx,maxy,w,l,r,ans,ansl,ansr,cnt,sum;
map<double,int> t1,t2;
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
int rev(int x)
{
int res = 0;
while(x)
{
res = res * 10 + x % 10;
x /= 10;
}
return res;
}
int main()
{
maxx = read(); maxy = read(); w = read(); ans = 1e10;
for(int i = 1; i <= maxx; i++) t1[1.0*i/rev(i)]++;
for(int i = 1; i <= maxy; i++) sum += t1[1.0*rev(i)/i];//求f(maxx,maxy)
if(sum < w) printf("%d\n",-1);//无解的情况
else
{
l = maxx, r = 0;
while(l && r <= maxy)
{
while(cnt < w && r <= maxy)
{
r++;
t2[1.0*rev(r)/r]++;
cnt += t1[1.0*rev(r)/r];//r指针对cnt的贡献
}
if(cnt >= w && r*l < ans)
{
ans = r*l;
ansl = l; ansr = r;
}
cnt -= t2[1.0*l/rev(l)];//l向左移对cnt的贡献
t1[1.0*l/rev(l)]--;
l--;
}
printf("%d %d\n",ansl,ansr);
}
return 0;
}