Блог пользователя _Faineant

Автор _Faineant, история, 11 месяцев назад, По-английски

Suppose Given a expression = (a*b*c)/(x*y*z) But as the value can be very big you have to output the final value mod 100000000000031 (1 <= a, b, c, x, y, z <= 10^18)

  • Проголосовать: нравится
  • 0
  • Проголосовать: не нравится

»
11 месяцев назад, # |
  Проголосовать: нравится +168 Проголосовать: не нравится

»
11 месяцев назад, # |
  Проголосовать: нравится -56 Проголосовать: не нравится
int mult(int x,int y){ return ((x%mod)*(y%mod))%mod;}
int expo(int x,int y){
  if (y==0) return 1;
  int a=expo(x,y/2);
  a=mult(a,a);
  if (a%2) a=mult(a,x);
  return a;
}
int divide(int x,int y){ return mult(x,expo(y,mod-2));}
//divide(mult(mult(a,b),c),mult(mult(x,y),z));

Multiplication is simple. Exponentiation using binary exponantion(works in log n time). Find modular inverse of the divisor with exponantion(fermat's little theorem) and multiply with x.

  • »
    »
    11 месяцев назад, # ^ |
    Rev. 3   Проголосовать: нравится +4 Проголосовать: не нравится

    Bruh moment, we can simply observe $$$a,b,c,x,y,z$$$ after modulo might have maximum value $$$mod-1 = 100000000000030$$$, this is around $$$10^{14}$$$. $$$10^{14}$$$ $$$*$$$ $$$10^{14}$$$ in the $$$mult$$$ function just explodes at $$$10^{28}$$$.

»
11 месяцев назад, # |
  Проголосовать: нравится +62 Проголосовать: не нравится

»
11 месяцев назад, # |
Rev. 2   Проголосовать: нравится +40 Проголосовать: не нравится

If you don't have __int128, you can avoid overflow when multiplying long long by something similar to binpow

long long mult(long long a, long long b)
{
    if (!b) return 0;
    long long r = mult(a, b/2);
    if (b&1) return ((r+r) % mod + a) % mod;
    return (r+r)% mod;
}

But it will take $$$O(\log b)$$$

»
11 месяцев назад, # |
  Проголосовать: нравится +24 Проголосовать: не нравится

Since $$$10^{14} < 2^{57}$$$, you can use the double hack from this blog:

uint64_t prod_double(const uint64_t x, const uint64_t y, const uint64_t m) {
	uint64_t c = (double)x * y / m;
	int64_t ans = int64_t(x * y - c * m) % int64_t(m);
	if (ans < 0)
		ans += m;
	return ans;
}
»
11 месяцев назад, # |
Rev. 4   Проголосовать: нравится +10 Проголосовать: не нравится

You can obviously create your own BigNum structure or something but that is too much work.

Instead we can shift our look from $$$\mathbb{Z}_{M}$$$, where $$$M=10^{14}$$$, to $$$\mathbb{Z}_{\sqrt{ M }}[\sqrt{M}]$$$. We will essentially keep two parts of each number. Maintaining elements of $$$\mathbb{Z}_{\sqrt{ M }}[\sqrt{ M }]$$$ is easy since we only keep two coefficients which are less than $$$\sqrt{ M }=10^7$$$.

Specifically, if $$$x\in \mathbb{Z}_{\sqrt{ M }}[\sqrt{ M }]$$$ then there are $$$a,b\in\mathbb{Z}_{\sqrt{ M }}$$$ such that $$$x=a+b\sqrt{ M }$$$, this will represent the literal $$$a+b\sqrt{ M }$$$ number in $$$\mathbb{Z}_{M}$$$. If $$$y\in\mathbb{Z}_{M}$$$ then with division there are $$$q,r$$$ such that $$$y=q\sqrt{ M }+r$$$ with $$$0\leq r<\sqrt{ M }$$$ and $$$0\leq q<\sqrt{ M }$$$ (else we would have $$$y\geq q\sqrt{M}\geq M>y$$$), so this is the representation of $$$y$$$ in $$$\mathbb{Z}_{\sqrt{ M }}[\sqrt{ M }]$$$.

Now we carry out the multiplication in $$$\mathbb{Z}_{\sqrt{ M }}[\sqrt{ M }]$$$ as

$$$(a +b\sqrt{ M })(c+d\sqrt{ M })=ac+bdM+(bc+ad)\sqrt{ M }=ac+(bc+ad)\sqrt{ M }$$$

,which we can do now since the multiplications fit in long long.

Keep in mind that this technique is simple and perhaps nice, but it is not a great solution for multiplying big numbers in general.

  • »
    »
    11 месяцев назад, # ^ |
      Проголосовать: нравится +10 Проголосовать: не нравится

    i have seen this trick once, here's code

    long long mmul(long long a,long long b,const long long& Mod)  {
    	long long lf=a*(b>>25LL)%Mod*(1LL<<25)%Mod;
            long long rg=a*(b&((1LL<<25)-1))%Mod;
    	return (lf+rg)%Mod;
    }
    

    there's no any annoying float number so it's 100% safe.