結果
| 問題 |
No.3242 Count 8 Included Numbers (Hard)
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-08-23 07:44:13 |
| 言語 | Java (openjdk 23) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 30,578 bytes |
| コンパイル時間 | 4,520 ms |
| コンパイル使用メモリ | 98,304 KB |
| 実行使用メモリ | 44,980 KB |
| 最終ジャッジ日時 | 2025-08-23 07:44:22 |
| 合計ジャッジ時間 | 8,599 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 5 |
| other | AC * 1 TLE * 1 -- * 18 |
ソースコード
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
class ModInt {
long mod;
long[] fac = new long[0];
long[] ifac = new long[0];
long[] inv = new long[0];
long[] fib = new long[0];
public ModInt(long mod) {
this.mod = mod;
}
void expand(int n) {
fac = new long[n];
ifac = new long[n];
inv = new long[n];
Arrays.fill(fac, 1);
Arrays.fill(ifac, 1);
Arrays.fill(inv, 1);
for (int i = 2; i < n; ++i) {
fac[i] = i * fac[i - 1] % mod;
inv[i] = mod - (mod / i) * inv[(int) (mod % i)] % mod;
ifac[i] = inv[i] * ifac[i - 1] % mod;
}
}
public long fac(int n) {
if (fac.length <= n) {
expand(Math.max(2 * fac.length, n + 1));
}
return fac[n];
}
public long ifac(int n) {
if (ifac.length <= n) {
expand(Math.max(2 * ifac.length, n + 1));
}
return ifac[n];
}
public long inv(int n) {
if (inv.length <= n) {
expand(Math.max(2 * inv.length, n + 1));
}
return inv[n];
}
public long comb(int n, int k) {
if (k < 0 || n - k < 0) return 0;
return fac(n) * ifac(k) % mod * ifac(n - k) % mod;
}
public long fib(int n) {
if (fib.length <= n) {
fib = new long[2 * Integer.highestOneBit(n)];
fib[0] = 1;
fib[1] = 1;
for (int i = 2; i < fib.length; ++i) {
fib[i] = (fib[i - 1] + fib[i - 2]) % mod;
}
}
return fib[n];
}
public long mul(Long...a) {
long ret = 1;
for (long v : a) ret = ret * v % mod;
return ret;
}
public long pow(long a, long n) {
return MathUtils.modPow(a, n, mod);
}
}
class FastScanner {
private static FastScanner instance = null;
private final InputStream in = System.in;
private final byte[] buffer = new byte[1024];
private int ptr = 0;
private int buflen = 0;
private FastScanner() {} // 外部から new できないようにする
public static FastScanner getInstance() {
if (instance == null) {
instance = new FastScanner();
}
return instance;
}
private boolean hasNextByte() {
if (ptr < buflen) return true;
ptr = 0;
try {
buflen = in.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return buflen > 0;
}
private int readByte() {
if (hasNextByte()) return buffer[ptr++];
else return -1;
}
private boolean isPrintableChar(int c) {
return 33 <= c && c <= 126;
}
public boolean hasNext() {
while (hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++;
return hasNextByte();
}
public String next() {
if (!hasNext()) throw new NoSuchElementException();
StringBuilder sb = new StringBuilder();
int b = readByte();
while (isPrintableChar(b)) {
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
public long nextLong() {
if (!hasNext()) throw new NoSuchElementException();
long n = 0;
boolean minus = false;
int b = readByte();
if (b == '-') {
minus = true;
b = readByte();
}
while (b >= '0' && b <= '9') {
n = n * 10 + (b - '0');
b = readByte();
}
return minus ? -n : n;
}
public int nextInt() {
return (int) nextLong();
}
public long[] nextLongs(int n) {
long[] a = new long[n];
for (int i = 0; i < n; ++i) {
a[i] = nextLong();
}
return a;
}
public int[] nextInts(int n) {
int[] a = new int[n];
for (int i = 0; i < n; ++i) {
a[i] = nextInt();
}
return a;
}
}
class PolynomialFp {
static long mod = 998244353;
public long[] f;
static long[][] bitreversedRoots = new long[30][];
static long[][] bitreversedInvRoots = new long[30][];
public PolynomialFp(long[] v) {
if (v != null) f = Arrays.copyOf(v, v.length);
}
public static PolynomialFp of(long[] v) {
return new PolynomialFp(v);
}
static long ADD(long a,long b) {
return a+b>=mod?a+b-mod:a+b;
}
static long SUB(long a,long b) {
return ADD(a,mod-b);
}
static void prepareRoots(int n) {
int sz = Integer.numberOfTrailingZeros(n);
if (bitreversedRoots[sz] != null) return;
long g = 3;
long root = MathUtils.modPow(g, (mod - 1) / n, mod);
long iroot = MathUtils.modInv(root, mod);
bitreversedRoots[sz] = new long[n];
bitreversedInvRoots[sz] = new long[n];
for (int n_ = n / 2; n_ >= 1; n_ /= 2, root = root * root % mod, iroot = iroot * iroot % mod) {
long w = 1;
long iw = 1;
for(int j=0;j<n_;++j) {
bitreversedRoots[sz][n_+j] = w;
bitreversedInvRoots[sz][n_+j] = iw;
w = w * root % mod;
iw = iw * iroot % mod;
}
int cur=0;
for(int j=0;j<n_;++j) {
if(cur<j) {
ArrayUtils.swap(n_+cur,n_+j,bitreversedRoots[sz]);
ArrayUtils.swap(n_+cur,n_+j,bitreversedInvRoots[sz]);
}
for (int k=n_/2;k>(cur^=k);k/=2) ;
}
}
}
public static void fft(long[] a, long g) {
int n=a.length;
{
int cur=0;
for (int i=0;i<n;++i) {
if (cur<i) {
a[i]^=a[cur];a[cur]^=a[i];a[i]^=a[cur];
}
for (int k=n/2;k>(cur^=k);k/=2) ;
}
}
for (int s=1;s<=n/2;s*=2) {
long mul=MathUtils.modPow(g,n/(2*s),mod);
for (int i=0;i<n;i+=2*s) {
long x=1;
for (int j=0;j<s;++j) {
long A=a[i+j];
long B=a[i+j+s]*x%mod;
a[i+j]=ADD(A,B);
a[i+j+s]=SUB(A,B);
x=x*mul%mod;
}
}
}
}
static void ifft(long[] a, long g) {
int n=a.length;
{
int cur=0;
for (int i=0;i<n;++i) {
if (cur<i) {
a[i]^=a[cur];a[cur]^=a[i];a[i]^=a[cur];
}
for (int k=n/2;k>(cur^=k);k/=2) ;
}
}
long invN = MathUtils.modInv(a.length, mod);
for (int s=1;s<=n/2;s*=2) {
long mul=MathUtils.modPow(g,n/(2*s),mod);
for (int i=0;i<n;i+=2*s) {
long x=(s==n/2?invN:1);
for (int j=0;j<s;++j) {
long A=a[i+j];
long B=a[i+j+s]*x%mod;
if(s==n/2)A=A*invN%mod;
a[i+j]=ADD(A,B);
a[i+j+s]=SUB(A,B);
x=x*mul%mod;
}
}
}
}
/**
* Scott, Michael. "A note on the implementation of the number theoretic transform." IMA International Conference on Cryptography and Coding. Cham: Springer International Publishing, 2017.
* @param a
*/
public static void fftTobitReversed(long[] a) {
int n=a.length;
int sz=Integer.numberOfTrailingZeros(a.length);
if (bitreversedRoots[sz] == null) prepareRoots(a.length);
for(int m = 1, t = n/2; m <= n/2; m *= 2, t /= 2) {
for(int i = 0, k = 0; i<m; ++i, k += 2*t) {
long S=bitreversedRoots[sz][m+i];
for(int j=k;j<k+t;++j) {
long u=a[j];
long v=a[j+t]*S%mod;
a[j]=ADD(u,v);
a[j+t]=SUB(u,v);
}
}
}
}
/**
* Scott, Michael. "A note on the implementation of the number theoretic transform." IMA International Conference on Cryptography and Coding. Cham: Springer International Publishing, 2017.
* @param a
*/
public static void ifftFromBitreversed(long[] a) {
long invN = MathUtils.modInv(a.length, mod);
int n=a.length;
int sz = Integer.numberOfTrailingZeros(n);
if (bitreversedInvRoots[sz] == null) prepareRoots(a.length);
for(int m = n/2, t = 1; m >= 1; m /= 2, t *= 2) {
for(int i = 0, k = 0; i<m; ++i, k += 2*t) {
long S=bitreversedInvRoots[sz][m+i];
if (m == 1) S=S*invN%mod;
for(int j=k;j<k+t;++j) {
long u=a[j];
long v=a[j+t];
if(m == 1) a[j] = (u + v) * invN % mod;
else a[j]=ADD(u,v);
a[j+t]=(u+mod-v)*S%mod;
}
}
}
}
public static long[] add(long[] a,long[] b) {
long[] ret=new long[Math.max(a.length, b.length)];
for (int i=0;i<ret.length;++i) ret[i]=ADD(i<a.length?a[i]:0,i<b.length?b[i]:0);
return ret;
}
public static long[] subtract(long[] a,long[] b) {
long[] ret=new long[Math.max(a.length, b.length)];
for (int i=0;i<ret.length;++i) ret[i]=ADD(i<a.length?a[i]:0,i<b.length?(mod-b[i]):0);
return ret;
}
static long[] mulFFT(long[] a,long[] b) {
int n=1;
while (n<a.length+b.length-1) n*=2;
a=Arrays.copyOf(a, n);
b=Arrays.copyOf(b, n);
prepareRoots(n);
fftTobitReversed(a);
fftTobitReversed(b);
for (int i = 0; i < a.length; ++i) a[i] = a[i] * b[i] % mod;
ifftFromBitreversed(a);
return a;
}
public static long[] mul(long[] a, long[] b) {
if (a.length + b.length - 1 <= 512) {
long[] ret=new long[a.length+b.length-1];
for(int i=0;i<a.length;++i) {
for(int j=0;j<b.length;++j) {
ret[i+j]+=a[i]*b[j];
ret[i+j]%=mod;
}
}
return ret;
} else {
return mulFFT(a, b);
}
}
public static long[] squared(long[] a) {
if (a.length <= 1024) {
long[] ret=new long[2 * a.length+a.length-1];
for(int i=0;i<a.length;++i) {
for(int j=i + 1;j<a.length;++j) {
ret[i+j]+=2*a[i]*a[j];
ret[i+j]%=mod;
}
}
for (int i = 0; i < a.length; ++i) {
ret[2 * i] += a[i] * a[i];
ret[2 * i] %= mod;
}
return ret;
} else {
int n=1;
while (n<a.length+a.length-1) n*=2;
a=Arrays.copyOf(a, n);
prepareRoots(n);
fftTobitReversed(a);
for (int i = 0; i < a.length; ++i) a[i] = a[i] * a[i] % mod;
ifftFromBitreversed(a);
return a;
}
}
public static long[] sqrt(long[] a) {
long[] ret=new long[a.length];
long b=MathUtils.modKthRoot(a[0],2,mod);
ret=mul(a,MathUtils.modInv(a[0],mod));
ret=log(ret);
ret=mul(ret,MathUtils.modInv(2,mod));
ret=exp(ret);
ret=mul(ret,b);
return ret;
}
public static long[] pow(long[] a,long m) {
int len = a.length;
if(m==0) {
long[] ret=new long[a.length];
ret[0]=1;
return ret;
} else if (m==1) {
return a;
} else if (m == 2) {
return squared(a);
}
int s=0;
while (s<a.length && a[s]==0) ++s;
if (s==a.length) return a;
if (s != 0) a=Arrays.copyOfRange(a, s, a.length);
long b=MathUtils.modInv(a[0], mod);
for (int i=0;i<a.length;++i) a[i]=b*a[i]%mod;
a=log(a);
for (int i=0;i<a.length;++i) a[i]=m%mod*a[i]%mod;
a=exp(a);
b=MathUtils.modPow(MathUtils.modInv(b, mod),m%(mod-1), mod);
for (int i=0;i<a.length;++i) a[i]=b*a[i]%mod;
long[]ret=new long[len];
if(s <= (len - 1) / m) {
for(long i = s * m; i<len && i - s * m < a.length;++i) {
ret[(int) i] = a[(int) (i - s * m)];
}
}
return ret;
}
static long[] log(long[] a) {
return integrate(resize(mul(differentiate(a), inv(a)),a.length));
}
/**
* A simple and fast algorithm for computing exponentials of power series. Alin Bostan
* @param a
* @return
*/
public static long[] exp(long[] a) {
long[] exp=new long[1];
long[] iexp=new long[1];
exp[0]=1;
iexp[0]=1;
long[] differeniatedA = differentiate(a);
long[] differentiatedExp = new long[2 * a.length];
for (int len=1;len<a.length;len*=2) {
long[] expFFT = resize(exp, 2 * len);
long[] iexpFFT = resize(iexp, 2 * len);
fftTobitReversed(expFFT);
fftTobitReversed(iexpFFT);
long[] x = new long[2 * len];
for (int i = 0; i < 2 * len; ++i) {
x[i] = iexpFFT[i] * iexpFFT[i] % mod * expFFT[i] % mod;
}
ifftFromBitreversed(x);
if (len !=1 ) iexp = resize(add(iexp, subtract(iexp, x)), len);
long[] q = resize(differeniatedA, len);
long[] qFFT = resize(q, len);
long[] expFFT2 = resize(expFFT, len);
fftTobitReversed(qFFT);
for (int i = 0; i < q.length; ++i) {
qFFT[i] = qFFT[i] * expFFT2[i] % mod;
}
ifftFromBitreversed(qFFT);
long[] s = new long[len];
for (int i = 0; i < len; ++i) {
s[i] = (exp[i] * i + mod - (i == 0 ? qFFT[len - 1] : qFFT[i - 1])) % mod;
}
long[] t = resize(mul(s, iexp), len);
t = multiplyByX(t, len - 1);
q = add(q, t);
q = resize(q, 2*len);
long[] u=divideByX(subtract(resize(a, 2*len), integrate(q)), len);
u = resize(u, 2*len);
fftTobitReversed(u);
for (int i = 0; i < u.length; ++i) u[i] = u[i] * expFFT[i] % mod;
ifftFromBitreversed(u);
exp = add(exp, multiplyByX(resize(u,len), len));
if (2 * len < a.length) {
for (int i = len; i < 2 * len; ++i) {
differentiatedExp[i - 1] = i * exp[i] % mod;
}
}
}
exp = resize(exp, a.length);
return exp;
}
static long[] exp2(long[] a) {
long[] exp=new long[a.length];
exp[0]=1;
for (int len=1;len<a.length;len*=2) {
long[] tmp=subtract(resize(a,2*len),log(resize(exp,2*len)));
++tmp[0];
exp=resize(mul(exp,tmp),2*len);
}
return exp;
}
static long[] differentiate(long[] a) {
long[] ret=new long[a.length];
for (int i=1;i<a.length;++i) ret[i-1]=i*a[i]%mod;
return ret;
}
static long[] integrate(long[] a) {
long[] ret=new long[a.length];
for (int i=0;i+1<a.length;++i) ret[i+1]=MathUtils.modInv(i+1,mod)*a[i]%mod;
return ret;
}
public static long[] inv(long[] a) {
long[] g=new long[1];
g[0]=MathUtils.modInv(a[0],mod);
for (int len=1;len<a.length;len*=2) {
long[] fftG=Arrays.copyOf(g, len * 4);
long[] fftA=new long[4 * len];
System.arraycopy(a, 0, fftA, 0, Math.min(2 * len, a.length));
prepareRoots(4*len);
fftTobitReversed(fftG);
fftTobitReversed(fftA);
for (int i = 0; i < fftG.length; ++i) {
fftG[i] = fftG[i] * fftG[i] % mod * fftA[i] % mod;
}
ifftFromBitreversed(fftG);
for (int i = 0; i < len; ++i) {
fftG[i] = g[i];
}
for (int i = len; i < 2 * len; ++i) {
if (fftG[i]!=0) fftG[i] = mod - fftG[i];
}
g=resize(fftG,2*len);
}
return g;
}
public static long[] mul(long[] a,long b) {
long[] ret=new long[a.length];
for (int i=0;i<a.length;++i) ret[i]=a[i]*b%mod;
return ret;
}
static long[] resize(long[] a,int len) {
return Arrays.copyOf(a, len);
}
public PolynomialFp mul(long a) {
return new PolynomialFp(mul(f, a));
}
public PolynomialFp mul(PolynomialFp poly) {
return new PolynomialFp(mul(f, poly.f));
}
public PolynomialFp sqrt() {
PolynomialFp poly = new PolynomialFp(null);
poly.f = exp(f);
return poly;
}
public PolynomialFp inv() {
PolynomialFp poly = new PolynomialFp(null);
poly.f = inv(f);
return poly;
}
public PolynomialFp subtract(PolynomialFp p) {
PolynomialFp poly = new PolynomialFp(null);
poly.f = subtract(f, p.f);
return poly;
}
public PolynomialFp add(PolynomialFp p) {
PolynomialFp poly = new PolynomialFp(null);
poly.f = add(f, p.f);
return poly;
}
/**
* f^src/(1-f) を返す
*/
public PolynomialFp geometricSeries(int src) {
return new PolynomialFp(mul(pow(f, src), inv(subtract(new long[] {1}, f))));
}
public PolynomialFp resize(int n) {
return new PolynomialFp(Arrays.copyOf(f, n));
}
/**
* f / x^n を返す。ただし、f は x^n を因数に持つとする
*/
public static long[] divideByX(long[] f, int repeat) {
return Arrays.copyOfRange(f, repeat, f.length);
}
public static long[] multiplyByX(long[] f, int repeat) {
long[] ret = new long[f.length + repeat];
for (int i = 0; i < f.length; ++i) ret[repeat + i] = f[i];
return ret;
}
public PolynomialFp multiplyByX(int repeat) {
PolynomialFp poly = new PolynomialFp(null);
poly.f = multiplyByX(f, repeat);
return poly;
}
public PolynomialFp pow(int n) {
return new PolynomialFp(pow(f, n));
}
public static PolynomialFp motzkin(int n) {
ModInt mi = new ModInt(mod);
PolynomialFp poly = new PolynomialFp(new long[n]);
poly.f[0] = 1;
poly.f[1] = 1;
for (int i = 2; i < n; ++i) {
poly.f[i] = ((2 * i + 1)*poly.f[i - 1]+(3*i-3)*poly.f[i-2]) % mod*mi.inv(i + 2);
poly.f[i] = (poly.f[i] % mod + mod)%mod;
}
return poly;
}
public static PolynomialFp centralTrinomials(int n) {
ModInt mi = new ModInt(mod);
PolynomialFp poly = new PolynomialFp(new long[n]);
poly.f[0] = 1;
poly.f[1] = 1;
for (int i = 2; i < n; ++i) {
poly.f[i] = 2*poly.f[i - 1]+3*poly.f[i-2]-(poly.f[i-1]+3*poly.f[i-2])*mi.inv(i);
poly.f[i] = (poly.f[i] % mod + mod)%mod;
}
return poly;
}
static void tr(Object...objects) {System.out.println(Arrays.deepToString(objects));}
}
class ArrayUtils {
public static void swap(int i, int j, int[]A){
if(i == j) return;
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
public static void swap(int i, int j, long[]A){
if(i == j) return;
long tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
/**
* swap A[i] and A[j]
*/
public static void swap(int i, int j, long[][]A){
if(i == j) return;
long[] tmp = Arrays.copyOf(A[i], A[i].length);
A[i] = A[j];
A[j] = tmp;
}
/**
* {1,2,..,n} から {start, start+1, .., end - 1} へのランダムな写像
* @param start (inclusive)
* @param end (exclusive)
* @param length
* @return
*/
public static int[] randomIntArray(int start, int end, int length) {
int[] ret = new int[length];
Random rnd=new Random();
Arrays.setAll(ret, i -> rnd.nextInt(start, end));
return ret;
}
/**
* {1,2,..,n} から {start, start+1, .., end - 1} へのランダムな写像
* @param start (inclusive)
* @param end (exclusive)
* @param length
* @return
*/
public static long[] randomLongArray(long start, long end, int length) {
long[] ret = new long[length];
Random rnd=new Random();
Arrays.setAll(ret, i -> rnd.nextLong(start, end));
return ret;
}
/**
* {start, start+1, .., end - 1}^n における辞書順の次の要素にAを変更する。
* @param start (inclusive)
* @param end (exclusive)
* @param length
* @return
*/
public static boolean nextArray(int[] A, int start, int end) {
int t=A.length-1;
while(t >= 0 && A[t]==end-1)--t;
if(t == -1)return false;
A[t]++;
for(int i=t+1;i<A.length;++i)A[i]=start;
return true;
}
public static int[] range(int startInclusive, int endExclusive) {
return IntStream.range(startInclusive, endExclusive).toArray();
}
public static void fill(long[][] a, long v) {
for(int i=0;i<a.length;++i) {
for(int j=0;j<a[i].length;++j) {
a[i][j]=v;
}
}
}
public static void fill(int[][] a, int v) {
for(int i=0;i<a.length;++i) {
for(int j=0;j<a[i].length;++j) {
a[i][j]=v;
}
}
}
public static void fill(char[][] a, char v) {
for(int i=0;i<a.length;++i) {
for(int j=0;j<a[i].length;++j) {
a[i][j]=v;
}
}
}
public static void fill(long[][][] a, long v) {
for(int i=0;i<a.length;++i) {
for(int j=0;j<a[i].length;++j) {
for(int k=0;k<a[i][j].length;++k) {
a[i][j][k]=v;
}
}
}
}
public static void fill(int[][][] a, int v) {
for(int i=0;i<a.length;++i) {
for(int j=0;j<a[i].length;++j) {
for(int k=0;k<a[i][j].length;++k) {
a[i][j][k]=v;
}
}
}
}
public static void reverse(int[] a) {
int s = 0;
int t = a.length - 1;
while (s < t) {
swap(s, t, a);
++s;--t;
}
}
public static void reverse(long[] a) {
int s = 0;
int t = a.length - 1;
while (s < t) {
swap(s, t, a);
++s;--t;
}
}
/**
* a[i] <= key となる最大の i を返す。aはソートされているとする。
* @param a
* @param key
* @return
*/
public static int floor(int[] a, int key) {
int ok = -1;
int ng = a.length;
while(ng - ok > 1) {
int m = (ok + ng) / 2;
if (a[m] <= key) ok=m;
else ng = m;
}
return ok;
}
/**
* key <= a[i] となる最小の i を返す。aはソートされているとする。
* @param a
* @param key
* @return
*/
public static int ceil(int[] a, int key) {
int ok = a.length;
int ng = -1;
while(ok - ng > 1) {
int m = (ok + ng) / 2;
if (a[m] >= key) ok=m;
else ng = m;
}
return ok;
}
/**
* a[i] <= key となる最大の i を返す。
* @param a
* @param key
* @return
*/
public static int floor(long[] a, long key) {
int ok = -1;
int ng = a.length;
while(ng - ok > 1) {
int m = (ok + ng) / 2;
if (a[m] <= key) ok=m;
else ng = m;
}
return ok;
}
/**
* key <= a[i] となる最小の i を返す。
* @param a
* @param key
* @return
*/
public static int ceil(long[] a, long key) {
int ok = a.length;
int ng = -1;
while(ok - ng > 1) {
int m = (ok + ng) / 2;
if (a[m] >= key) ok=m;
else ng = m;
}
return ok;
}
/**
* 配列を連結する
* @param a
* @return
*/
public static long[] concat(long[]... a) {
int len = 0;
for (int i = 0; i < a.length; ++i) {
len += a[i].length;
}
int src = 0;
long[] ret = new long[len];
for (int i = 0; i < a.length; ++i) {
for (int j = 0; j < a[i].length; ++j) {
ret[src + j] = a[i][j];
}
src += a[i].length;
}
return ret;
}
public static int[] concat(int[]... a) {
int len = 0;
for (int i = 0; i < a.length; ++i) {
len += a[i].length;
}
int src = 0;
int[] ret = new int[len];
for (int i = 0; i < a.length; ++i) {
for (int j = 0; j < a[i].length; ++j) {
ret[src + j] = a[i][j];
}
src += a[i].length;
}
return ret;
}
public static boolean equals(long[] a, long[] b) {
if (a.length != b.length) return false;
for (int i = 0; i < a.length; ++i) if (a[i] != b[i]) return false;
return true;
}
/**
* a.length が2冪を仮定
* @param a
*/
public static void bitReveseOrder(long[]a) {
int n = a.length;
int cur=0;
for (int i=0;i<n;++i) {
if (cur<i) {
swap(i, cur, a);
}
for (int k=n/2;k>(cur^=k);k/=2) ;
}
}
/**
* a.length が2冪を仮定
* @param a
*/
public static void bitReveseOrder(int[]a) {
int n = a.length;
int cur=0;
for (int i=0;i<n;++i) {
if (cur<i) {
swap(i, cur, a);
}
for (int k=n/2;k>(cur^=k);k/=2) ;
}
}
/**
* b[i] = a[1] + a[2] + .. + a[i]
* @param a
* @param mod
* @return
*/
public static long[] modPrefixSum(long[] a, long mod) {
long[] b = new long[a.length];
for (int i = 0; i < b.length; ++i) {
b[i] = ((i == 0 ? 0 : b[i - 1]) + a[i]) % mod;
}
return b;
}
/**
* b[i] = a[1] + a[2] + .. + a[i]
* @param a
* @param mod
* @return
*/
public static long[] prefixSum(long[] a) {
long[] b = new long[a.length];
for (int i = 0; i < b.length; ++i) {
b[i] = (i == 0 ? 0 : b[i - 1]) + a[i];
}
return b;
}
public static long[][] copy(long[][] a) {
long[][] b = new long[a.length][];
Arrays.setAll(b, i -> Arrays.copyOf(a[i], a[i].length));
return b;
}
public char[][] rightRotateGrid(char[][] a) {
char[][] b = new char[a[0].length][a.length];
for (int i = 0; i < a.length; ++i) {
for (int j = 0; j < a[i].length; ++j) {
b[j][a.length-1-i]=a[i][j];
}
}
return b;
}
}
class MyPrintWriter extends PrintWriter {
public MyPrintWriter(PrintStream out) {
super(out);
}
public void print(long[] a) {
print(a, " ");
}
public void print(int[] a) {
print(a, " ");
}
public void print(int[] a, String separator) {
for (int i = 0; i < a.length; ++i) {
super.print(a[i]+(i==a.length-1?"\n":separator));
}
}
public void print(long[] a, String separator) {
for (int i = 0; i < a.length; ++i) {
super.print(a[i]+(i==a.length-1?"\n":separator));
}
}
public void printlnYesNo(boolean flag) {
println(flag?"Yes":"No");
}
}
class MathUtils {
/**
* ニュートン法を用いて、sqrtを計算する。
* x-f(x)/f'(x)=x-(x**2-n)/2x=x-x/2+n/(2x)=(x*x+n)/(2x)
*/
public static int sqrt(long a) {
if(a==0)return 0;
long ret=a;
while(true) {
long nret=(ret+a/ret)/2;
if(nret>=ret)break;
ret=nret;
}
return (int)ret;
}
public static long ceil(long a, long b) {
return (a + b - 1) / b;
}
public static long gcd(long a, long b) {
if (a == 0) return b;
return gcd(b % a, a);
}
public static long modPow(long a, long n, long mod) {
if (n == 0) return 1;
return modPow(a * a % mod, n / 2, mod) * (n % 2 == 1 ? a : 1) % mod;
}
public static long pow(long a, long n) {
if (n == 0) return 1;
return pow(a * a, n / 2) * (n % 2 == 1 ? a : 1);
}
public static long modKthRoot(long a, long k, long mod) {
if (k == 0) return a == 1 ? 1 : -1;
if (k >0 && a == 0) return 0;
long g = gcd(k, mod - 1);
if (modPow(a, (mod - 1) / g, mod) != 1)
return -1;
a = modPow(a, modInv(k / g, (mod - 1) / g), mod);
for (long div = 2; div * div <= g; ++div) {
int sz = 0;
while (g % div == 0) {
g /= div;
++sz;
}
if (sz > 0) {
long b = peth_root(a, div, sz, mod);
a = b;
}
}
if (g > 1)
a = peth_root(a, g, 1, mod);
return a;
}
private static long peth_root(long a, long p, int e, long mod) {
long q = mod - 1;
int s = 0;
while (q % p == 0) {
q /= p;
++s;
}
long pe = modPow(p, e, mod);
long ans = modPow(a, ((pe - 1) * modInv(q, pe) % pe * q + 1) / pe, mod);
long c = 2;
while (modPow(c, (mod - 1) / p, mod) == 1)
++c;
c = modPow(c, q, mod);
HashMap<Long, Integer> map = new HashMap<>();
long add = 1;
int v = (int) Math.sqrt(p) + 1;
long mul = modPow(c, v * modPow(p, s - 1, mod - 1), mod);
for (int i = 0; i <= v; ++i) {
map.put(add, i);
add = add * mul % mod;
}
mul = modInv(modPow(c, modPow(p, s - 1, mod - 1), mod), mod);
out: for (int i = e; i < s; ++i) {
long err = modInv(modPow(ans, pe, mod), mod) * a % mod;
long target = modPow(err, modPow(p, s - 1 - i, mod - 1), mod);
for (int j = 0; j <= v; ++j) {
if (map.containsKey(target % mod)) {
int x = map.get(target % mod);
ans = ans * modPow(c, (j + v * x) * modPow(p, i - e, mod - 1) % (mod - 1), mod) % mod;
continue out;
}
target = target * mul % mod;
}
throw new AssertionError();
}
return ans;
}
public static long modInv(long a, long mod) {
return modPow(a,mod-2,mod);
}
public static long[] modAdd(long[] a, long[] b, long mod) {
if (a.length != b.length) throw new AssertionError();
long[] c = new long[a.length];
for (int i = 0; i < a.length; ++i) {
c[i] = a[i] + b[i];
if (c[i] >= mod) c[i] -= mod;
}
return c;
}
public static long[] modMul(long[] a, long scalar, long mod) {
long[] b = new long[a.length];
for (int i = 0; i < a.length; ++i) {
b[i] = a[i] * scalar % mod;
}
return b;
}
public static long[] modSub(long[] a, long[] b, long mod) {
if (a.length != b.length) throw new AssertionError();
long[] c = new long[a.length];
for (int i = 0; i < a.length; ++i) {
c[i] = a[i] - b[i];
if (c[i] < 0) c[i] += mod;
}
return c;
}
/**
* k = floor(N/i) が等しい区間 1 <= l <= i <= r <= N について組 [l, r, k] を k の昇順(iの降順)に返す。
*/
public static ArrayList<long[]> floorRange(long N) {
ArrayList<long[]> range=new ArrayList<>();
long quotient=1;
while(true) {
long upper=N/quotient;
long lower=N/(quotient+1)+1;
range.add(new long[] {lower, upper, quotient});
if(lower==1)break;
quotient=N/(lower-1);
}
return range;
}
public static String toFibonacciBaseString(long n) {
long[] F=new long[92];
F[0]=F[1]=1;
for(int i=2;i<F.length;++i) {
F[i]=F[i-1]+F[i-2];
}
StringBuilder sb=new StringBuilder("");
for(int i=F.length-1;i>=0;--i) {
if(n>=F[i]) {
sb.append("1");
n-=F[i];
}else {
sb.append("0");
}
}
return sb.toString();
}
/**
* a = sum[i] b[i] 10^i となる b を返す
* @param a
* @return
*/
int[] digitsArray(long a) {
int[] ret=new int[40];
for(int i=0;i<40;++i) {
ret[i]=(int)(a%10);
a/=10;
if(a==0) return Arrays.copyOf(ret, i + 1);
}
throw new AssertionError();
}
static void tr(Object...objects) {
System.out.println(Arrays.deepToString(objects));
}
}
class Main implements Runnable { // Runnableを実装する
public static void main(String[] args) {
new Thread(null, new Main(), "", 1024 * 1024 * 1024).start(); // 1024MBスタックを確保して実行
}
final long mod=998244353;
public void run() {
FastScanner sc = FastScanner.getInstance();
MyPrintWriter pw = new MyPrintWriter(System.out);
String S=sc.next();
char[]xx=S.toCharArray();
int[]n=new int[xx.length];
Arrays.setAll(n, i->(int)(xx[i]-'0'));
long[]poly=new long[10];
long[]mahler=new long[10];
Arrays.fill(poly, 1);
Arrays.fill(mahler, 1);
mahler[8] = 0;
long sub=nthMahler(S, 10, new long[] {1}, PolynomialFp.mul(mahler, poly), mod);
sub=(mod-1+sub)%mod;
long all=new BigInteger(S).mod(BigInteger.valueOf(mod)).longValue();
System.out.println((all+mod-sub)%mod);
pw.close();
}
static void tr(Object...objects) {System.out.println(Arrays.deepToString(objects));}
/**
* [x^n] poly(x)mahler(x)mahler(x^m)mahler(x^{2m})...
* @param poly
* @param mahler
* @return
*/
long nthMahler(String n, int m, long[] poly, long[] mahler, long mod) {
if (mahler[0] != 1) throw new AssertionError();
if (m == 10) return nthMahlerBase10(n, poly, mahler, mod);
BigInteger nBigInt=new BigInteger(n);
BigInteger mBigInt=BigInteger.valueOf(m);
long[] g = Arrays.copyOf(poly, poly.length);
while (!nBigInt.equals(BigInteger.ZERO)) {
int r = nBigInt.mod(mBigInt).intValue();
long[] f = PolynomialFp.mul(g, mahler);
long[] ng = new long[1 + (f.length - 1 - r) / m];
for (int i = r; i < f.length; i += m) {
ng[i / m] = f[i];
}
g = ng;
nBigInt = nBigInt.divide(mBigInt);
}
return g[0];
}
/**
* [x^n] poly(x)mahler(x)mahler(x^m)mahler(x^{2m})...
* @param poly
* @param mahler
* @return
*/
long nthMahlerBase10(String n, long[] poly, long[] mahler, long mod) {
if (mahler[0] != 1) throw new AssertionError();
int m = 10;
long[] g = Arrays.copyOf(poly, poly.length);
for (int i = 0; i < n.length(); ++i) {
int r = (int) (n.charAt(i) - '0');
long[] f = PolynomialFp.mul(g, mahler);
long[] ng = new long[1 + (f.length - 1 - r) / m];
for (int j = r; j < f.length; j += m) {
ng[j / m] = f[j];
}
g = ng;
}
return g[0];
}
}