Intro
Addition modulo p is simple, just add the two numbers, then modulo the result. Multiplication is also simple — multiply then take modulo. However, subtraction cannot be done in this way. This caught out quite a few people in today's Educational round due to the weak pretests on problem C, and I wanted to make a blog about it so that hopefully this doesn't happen again.
The pitfall
Take this: 306786744 submission for example. There were plenty that did exactly the same thing, but I just picked this one because it's relatively short. Specifically, let's look at the following line of code:
ans = (ans+one-onemain)%MOD;
You might think — what could possibly go wrong here? You're just adding and subtracting some numbers and taking modulo. Quite a lot as it turns out. The issue is that in C++, the modulo operator does not work well with negative numbers, and will just give you a negative remainder. This causes a problem because when I carefully (read: ask GPT to) construct a countertest, I can exploit this incorrect behaviour of the modulo operator, and cause your code to output the wrong answer. The way it's done is that I try to create a testcase where onemain is large, and ans+one is small (in this case, has just been wrapped round by the modulo operator). Now, we've got a negative number, and we take modulo, so now the calculation just outputs a negative number and you got hacked.
How to ensure this doesn't happen to you?
Two ways:
Make sure to add MOD after subtracting two numbers, then take the modulo of the result. If we correct the code to
ans = (ans+one-onemain+MOD)%MOD;
, everything passes.Just use a modular arithmetic template. It comes up in so many problems, so I think it's a waste of time writing all this MOD stuff out every time, and it could save you a lot of WA, or if you're REALLY unlucky like today, FST.
Hope this helps someone!
Such an informative blog I wonder why the downvotes?
downvoted by the people who got hacked I guess?
bro thinks he's djm03178
or maybe find a MODINT template online?
As a python user, this hack doesn't work on us fortunately.
finally first winning for Python?
happy snake noises
do you have the template in java plz share it if you have
i dont use java :( but it's just simple class and overloading operator, u can make it by urself :)
This whole problem only exists due to the weak pretests in C today, which I already ranted about in the blog post itself so I will go more into depth about this issue here instead
but to elaborate in detail you can construct something such that
"one" in the case of the example becomes a number slightly greater than or equal to M
and making sure there are more 1s before than this number
some examples i used, you could do this by getting binary representation and whenever seeing a 1 in it adding 1 2 (or 3 2 depending on the sol you're hacking), and when seeing a 0 add a 2
It's pretty hard to expect a test to randomly hack it when a more natural solution without subtraction exists, and they probably didn't notice it during contest preparation.
Even just making ones=MOD should fail
1 1 1 2 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 3
ans = (ans+one-onemain+MOD)%MOD;
it would still be wrong.right:
ans = ((ans+one-onemain)%MOD+MOD)%MOD
.ans = (ans+one-onemain+MOD)%MOD;
is enough whenonemain
is guaranteed to be less thanMOD
, which is the case for this code.Yes, but in order not to take any risk, it's better to do what I said.
A more general advice would be to keep every value used for calculation within $$$[0,MOD)$$$. This way, adding a single
MOD
after each subtraction always works, and performing modulo multiple times can be very slow sometimes.