Sorry for misjudging the difficulty of problem D, but we still hope you enjoyed the problems!
Author: CutieSmileHaruka
$$$a \oplus 1$$$ is equivalent to $$$a - 1$$$ when $$$a$$$ is an odd number, otherwise it is equivalent to $$$a + 1$$$.
If you apply $$$a \gets a \oplus 1$$$ when $$$a$$$ is odd, then that operation should make $$$a = b$$$.
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
int t, a, b, x, y;
int main(){scanf("%d", &t);
while (t --){scanf("%d%d%d%d", &a, &b, &x, &y);
if (a > b) printf("%d\n", (a ^ 1) == b ? y : -1);
else {int c0 = b - a, c1 = (b + 1 >> 1) - (a + 1 >> 1);
printf("%lld\n", y > x ? 1ll * c0 * x : 1ll * (c0 - c1) * x + 1ll * c1 * y);}
}
}
Author: Lyz09
Transform $$$(p_x,p_y)$$$ to $$$(q_x,q_y)$$$ into an operation where $$$a_i$$$ is the distance between the two points.
When $$$n+1=3$$$, there is a solution if and only if the largest $$$a_i$$$ is less than or equal to the sum of the remaining $$$a_i$$$.
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 100010
#define int long long
int t,n,sx,sy,tx,ty;
double a[N];
signed main()
{
cin>>t;
while(t--)
{
cin>>n;
cin>>sx>>sy>>tx>>ty;
for(int i=1;i<=n;i++)
cin>>a[i];
a[++n]=sqrt((sx-tx)*(sx-tx)+(sy-ty)*(sy-ty));
sort(a+1,a+n+1);
double sum=a[n];
for(int i=1;i<n;i++)
sum-=a[i];
if(sum<=0)
puts("Yes");
else
puts("No");
}
}
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
#define int long long
int t,n,sx,sy,tx,ty,a[N];
signed main()
{
cin>>t;
while(t--)
{
cin>>n;
cin>>sx>>sy>>tx>>ty;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1);
int p=(sx-tx)*(sx-tx)+(sy-ty)*(sy-ty);
if(p>a[n]*a[n])
{
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=a[i];
if(sum*sum>=p)
break;
}
if(sum*sum>=p)
puts("Yes");
else
puts("No");
}
else
{
int sum=a[n];
for(int i=1;i<=n-1;i++)
sum-=a[i];
if(sum<=0||sum*sum<=p)
puts("Yes");
else
puts("No");
}
}
}
Author: lizhous Preparation: Lyz09
For odd $$$n$$$, setting $$$a_i = l$$$ for all $$$i$$$ suffices.
For even $$$n$$$, there does not exist a valid array $$$a$$$ that satisfies $$$a_1=a_2=\dots=a_{n-1}=l$$$.
For even $$$n$$$, try to make $$$a_1=a_2=\dots=a_{n-2}=l$$$.
#include<iostream>
using namespace std;
#define int long long
int t,n,l,r,k;
signed main()
{
cin>>t;
while(t--)
{
cin>>n>>l>>r>>k;
if(n%2==1)
{
cout<<l<<endl;
continue;
}
if(n==2)
{
cout<<-1<<endl;
continue;
}
int res=1;
bool fl=0;
while(res<=r)
{
if(res>l)
{
fl=1;
if(k<=n-2)
cout<<l<<endl;
else
cout<<res<<endl;
break;
}
res*=2;
}
if(!fl)
cout<<-1<<endl;
}
}
Author: CutieSmileHaruka
Calculating the weight of a single valid sequence is difficult. Try another way?
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int N = 5005;
int t, n, mod, f[N][N], ans;
inline void add(int& x, int y){x += y;if (x >= mod) x -= mod;}
int main(){scanf("%d", &t);
while (t --){scanf("%d%d", &n, &mod), f[0][0] = 1, ans = 0;
for (int i = 1;i <= n;i ++) for (int j = 0;j <= i;j ++) f[i][j] = 0;
for (int i = 1;i <= n;i ++)
for (int j = 0, now;j < i;j ++)
if (now = f[i - 1][j]) add(f[i][j + 1], now),
f[i][j] = (f[i][j] + (n - i + 1ll) * (j + 1) * now) % mod;
for (int j = 0;j <= n;j ++) add(ans, f[n][j]);printf("%d\n", ans);
}
}
Author: ma2021tyoi0037
As same as the title, can you find a simple necessary constraint?
Consider $$$a_i = 0$$$ first.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct stu{
int x;
ll dp;
};
int n,a[100007],b[100007];
vector<stu> f,g;
const ll inf=0x3f3f3f3f3f3f3f3f;
void Subt(){
cin>>n;
for(int i=1;i<n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
a[n]=0;
f.clear();
f.push_back((stu){0,0});
for(int i=1;i<=n;i++){
int x=0;
g.clear();
for(int j=30;j>=-1;j--){
int y=x|a[i-1]|a[i];
if(j!=-1) y|=(1<<j);
if(y>=b[i]){
ll mn=inf;
for(stu l:f){
if((l.x&y)!=a[i-1]) continue;
mn=min(mn,l.dp+y-b[i]);
}
if(mn<inf) g.push_back((stu){y,mn});
}
x|=((1<<j)&b[i]);
}
swap(f,g);
}
ll mn=inf;
for(stu l:f) mn=min(mn,l.dp);
if(mn<inf) cout<<mn<<endl;
else cout<<-1<<endl;
return;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T=1;
cin>>T;
while(T--) Subt();
}
Author: lizhous
Try to determine the vertex where you were before the last move.
I just need to calculate the earliest time I can reach the end vertex.
What form of moving path from the starting vertex to the end vertex is considered optimal?
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<set>
#include<queue>
#include<unordered_map>
#include<map>
#define int long long
using namespace std;
int n,st,dist[1000002],w[1000001],tim[1000001],ans;
vector <int> g[1000001];
int f[1000001];
queue<int> q;
void getdis(int u,int fa)
{
for(int v:g[u])
{
if(v==fa) continue;
dist[v]=dist[u]+1;
getdis(v,u);
}
}
void dfs(int u,int fa,int k,bool inn,int hei,int TIM,int dis)
{
if(hei<0)
{
TIM=max(TIM,(-hei+1)/2*2+k*2+dis);
}
if(TIM<=dist[u])
{
ans=max(ans,dist[u]);
}
else
{
return;
}
for(int v:g[u])
{
if(v==fa) continue;
if(inn&&w[u]!=w[v])
{
dfs(v,u,min(k,tim[v]),inn,hei+w[v],TIM+1,dis+1);
}
else
{
dfs(v,u,k,0,hei+w[v],TIM+1,dis+1);
}
}
}
int T;
signed main()
{
ios::sync_with_stdio(false);
cin>>T;
int cnt=0;
while(T--)
{
cnt++;
cin>>n>>st;
for(int i=1;i<=n;i++)
{
dist[i]=0;
tim[i]=1000000000;
g[i].clear();
f[i]=-10000000000000000;
}
ans=0;
for(int i=1;i<=n;i++)
{
cin>>w[i];
}
for(int i=1,u,v;i<n;i++)
{
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
getdis(1,-1);
for(int i=1;i<=n;i++)
{
for(int v:g[i])
{
if(w[i]==1&&w[v]==1)
{
tim[i]=0;
q.push(i);
break;
}
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int v:g[u])
{
if(tim[v]==1000000000&&w[v]!=w[u])
{
tim[v]=tim[u]+1;
q.push(v);
}
}
}
dfs(st,-1,tim[st],1,w[st],1,1);
cout<<ans+(dist[st]&1)-1<<'\n';
}
}









Auto comment: topic has been updated by CutieSmileHaruka (previous revision, new revision, compare).
D is interesting and easy to code.
ABC were easy
D was just chinese for me. Hope to learn and be better for next contest. Learnt about the reverse the Summation Technique.
Thanks for the problems :)
An interesting thing is, I thought you were referring to the AtCoder Beginner Contest.
I see that problems so hard, and editorial too
Me too and i only finished A during the contest……
The one who really wanna win never smile,hahaha
Can someone explain the solution to E in simpler words? "preceding bits", "next bit" are context-less. And what is meant by "Numbers not on this step are certainly useless"?
Hope you guys can check out the solution explanations I wrote — I've shared some of my thoughts on these problems and approaches to solving them.
My solution of Problem A, D, E, F
I very welcome any feedback on areas that need improvement.
why in problem C don't check if res&l==0 what if res is larger but still has bit equal to l
If i got your question right, if res is bigger than L then res & L must equal 0 cause res is a power of 2 consisting 1 bit that is set and isnt in L's bit set
Could someone make an editorial for the editorial of D please? What is the meaning behind mentioned segments? What are the $$$l_i$$$ and $$$r_i$$$ stands for?..
This is how I understand it:
For each valid sequence $$$a$$$ of length $$$n$$$, $$$f(a)$$$ is the number of arrays $$$p_0 \gt p_1 \gt ... \gt p_{k-1} \gt 0$$$ for all $$$k \in \{0,1,...,n\}$$$ such that $$$p_0,...,p_{k-1}$$$ is an order of removing tokens based on sequence $$$a$$$.
We wouldn't compute our answer directly as:
over all $$$(n+1)!$$$ array. What we do is denote $$$g(p)$$$ as the number of valid sequences $$$a$$$ that possess $$$p$$$ as an order of removing tokens, then compute:
over all $$$p$$$. Turns out $$$g(p) = \prod_{i=0}^{k-1}p_i(n-p_i + 1 - i)$$$ as in the editorial. Proof is shown below.
For each array $$$p_0 \gt p_1 \gt ... \gt p_{k-1} \gt 0$$$, we'll consider any valid sequence $$$a$$$ that owns $$$p$$$ as an order of token removal. Suppose $$$a_{x_0}, a_{x_1}, ... a_{x_{k-1}}$$$ corresponds to such $$$p$$$, in other words on the $$$x_j$$$ turn, we remove token $$$p_j$$$. ($$$x_0,...x_{k-1}$$$ are all indices where $$$a \gt 0$$$)
Consider $$$p_0$$$, based on the problem statement, $$$p_0 \in [a_{x_0}, x_0]$$$. With this inequality, we conclude that there are $$$p_0(n - p_0 + 1)$$$ ways to choose value and position for $$$a_{x_0}$$$ accordingly. With the same argument you can obtain $$$g(p) = \prod_{i=0}^{k-1}p_i(n-p_i + 1 - i)$$$. The operator "$$$-i$$$" is due to the fact that we have already chosen $$$i$$$ positions $$$x_0,...x_{i-1}$$$ beforehand.
I assume what the author meant was that $$$l_i, r_i$$$ are the number of ways to choose value and index for $$$a_{x_i}$$$ respectively.
Finally for the dp part. For a fixed $$$n$$$, our answer is sum of:
over all $k \in \{ 0,1,...,n \}$. The definition of $$$f[i][j]$$$ array is:
"Value of function $$$h$$$, evaluated over arrays $$$p$$$ of length $$$i-j$$$, whose last element $$$p_{i-j-1} \geq n-i+1$$$".
Now $$$f[i][j]$$$ is computed by considering two distinct cases of $$$p[i-j-1]$$$, if it's equal to $$$n-i+1$$$ then by substituting in, we obtain $$$(n-i+1)(j+1)f[i-1][j]$$$ ($$$i-1$$$ because then the lowerbound of $$$p[i-j-2]$$$ is $$$n-i+2$$$). For the latter case, $$$p[i-j-1] \geq n-i+2$$$ so we obtain $$$f[i-1][j-1]$$$.
So $$$f[n][j]$$$ would be our answer computed on arrays $$$p$$$ of length $$$n-j$$$, whose last element has lowerbound of 1 (a.k.a $$$h[n-j]$$$). Taking the sum for $$$j$$$ from $$$0$$$ to $$$n$$$ would give us the answer.
I thought to myself why didn't the author simply choose $$$i$$$ as the length of array $$$p$$$ and $$$j$$$ as the lowerbound for $$$p_{i-1}$$$ but turns out that recurrence would ask us to compute $$$f[i][j]$$$ via $$$f[i-1][j+1]$$$ and $$$f[i][j+1]$$$ which is very annoying.
Edit: Nah not annoying at all, even easier :> 327921559
Great explanation.
This is how i understand it: (after the Umnik video) You can convert it to a take not take dp and since a token can only be picked from positions greater than equal to where it resides it gives us the idea that we can start iterating from the back.
let dp[i][j] be the number of ways to pick j tokens from [i,n] tokens; now for the transitions if we dont choose the ith token then dp[i][j] is just dp[i+1][j] else we choose the ith token now there are (n+1-i) positions who could have chosen the ith token (remember that a particular token can only be picked up by a larger or equal position) and out of these (n+1-i), j-1 have already been used so that leaves (n+2-(i+j)) positions on the right that can choose this token.
Now this is where l[i] comes in because there can be multiple different arrays that pick the tokens in the same order and we have to count all of them!
for a position to be able to pick token i the value at that position should less than equal to i (remember the range [a[k],k] so there are i options for this left boundary thus for the take transition we get : dp[i][j] += dp[i+1][j-1]*(i)*(n+2-(i+j)).
hope it's clear : 327920799
I am not even sure if this is just language gap.
Hint doesn't help at all.
They assume too much of stuff as obvious, easy to catch and simply skip over it. It really hurts my two brain cells.
Why can't i output l when k <= 2 in C?
Because there are cases where no solution exists (especially when $$$n=2$$$)? I think in the example input there are already cases like this.
I mean when n > 2 and n is even, outputting l when k <= 2 or when k < n — 1 can both be correct. For example, when n = 6, l = 1, r = 2, both a = [1, 1, 2, 2, 2, 2] and a = [1, 1, 1, 1, 2, 2] are OK.
If you have accounted for cases where $$$n \gt 2$$$ but there is still no solution (say, $$$n=6$$$, $$$l=r=1$$$) and printed $$$-1$$$ in those cases, then yes, when $$$k\le2$$$ it is obviously that the answer is $$$l$$$.
(By the way, in your example only $$$[1,1,1,1,2,2]$$$ is correct.)
The problem statement says (and I quote): "You need to find the lexicographically smallest array a of length n".
So a = [1,1,1,1,2,2] is the only correct array here.
This means that the only correct case when you output l is k < n-1.
Thanks! I understand now,I did not notice that before.
in problem C ,if the choice for an or an-1 exceeds r then why dont we try to find the array with less number of terms equal to l and allowing more freedom of choice for subsequent terms?
The thing is, if there exists a solution, then there exists a solution with the first $$$n-2$$$ elements equal to $$$l$$$.
It was proven in the tutorial that if $$$r$$$ does not have a different highest bit from $$$l$$$, then there is no solution. Thus, we assume $$$r$$$ has a different highest bit from $$$l$$$ from now on.
Let $$$t=100...0_2$$$ be the smallest number with a different highest bit from $$$l$$$ (then $$$t\le r$$$). Consider the array $$$[l, l, l, ..., l, t, t]$$$ of ($$$n-2$$$) $$$l$$$'s and $$$2$$$ $$$t$$$'s. Note that the AND of this array is $$$0$$$.
Consider the bit of $$$l$$$ at position $$$p$$$. If it is $$$0$$$, obviously the result of the XOR at this position is $$$0$$$. If it is $$$1$$$, their XOR will also be $$$0$$$ (because we have an even number of $$$l$$$'s). XORing $$$0$$$ with $$$2$$$ zero bits of $$$t$$$'s results in a $$$0$$$, and thus the XOR of the array equals to its AND. Hence, $$$[l, l, l, ..., l, t, t]$$$ is always a valid solution, if there is one.
I think the gap between problem C and problem D difficulties was way big.
Can anyone help me with B?
you can try to make a polygon with the edges
Hi, could someone explain the other solutions to Problem D? I noticed different approaches, and some of the code looks quite interesting. I'd love to understand the reasoning behind them.
Such as following solution
This is essentially the same solution as the editorial. However, this dp is only storing the value of the current and previous rows, hence the dp and ndp.
Did anyone solve B by managing rings that are covered as we add a's gradually? For example, if a=[5,3,...] after step 2 we know that every point with distance [5-3, 5+3] is covered, simply by looking at how circle centered at distance 5 and radius 3 is moving around the circle with distance 5.
I did. Maintain the smallest distance and larges distance that can be covered by circle.
can you share the solution as I have been trying the same way but getting wrong answer on test case 2.
https://mirror.codeforces.com/contest/2119/submission/327566232
This is what I was looking for, thanks. What's confusing me a bit is that you start with this max_dis = begin_dis, min_dis = begin_dis; How does it make sense to start with a circle with radius begin_dis? When I was trying to solve in similar way, my starting circle was a0. Then in the end i would check if min_dis and max_dis cover begin_dis. Seems to me that you artificially added radius begin_dis at the start, but how is that valid, since that is an extra move?
What I thought is a little bit different from yours, but very similar. I consider the initial distance between the starting point and the target point to be
begin_dis. With each move, the distance between the current point and the target point is updated (which is depend on circle). Then the condition for reaching the target is whendistance == 0.The same as me.
hey there, My Sub: https://mirror.codeforces.com/contest/2119/submission/332222621
can you help me identify the issue with this solution? I am building a pref sum and breaking the whole array into 2 sides of triangle and measuring the min and max distance that can be covered. But it gives WA on 1879th case of test2.
can you please elaborate your approach for b
One mindless solution to Problem A is to apply Dijkstra's algorithm, whose complexity is acceptable.
xd
How is the answer 37 for n=3 in D? I am getting 36 in 24 combinations Please help
~~~~~~~~~~~~~~~~
include
using namespace std;
define ll long long
int main() { int t; cin >> t; while(t--){
ll n, l, r, k , ans; cin >> n >> l >> r >> k; bool found = 0; if(n == 2){ cout << -1 << "\n"; }else if(n % 2 == 1){ cout << l << "\n"; }else{ // n even if(k <= n - 2){ cout << l << "\n"; }else{ for(ll i = l + 1; i <= r; i++ ){ if((i & l) == 0){ ans = i; found = true; break; } } if(found){ cout << ans << "\n"; }else{ cout << -1 << "\n" ; } } }}
}
~~~~~~~~~~~~~~~~~~~~~~ Why is this code wrong on test 3997? How can I know this test case?
Learn to paste code in expandable section or share solution in
[submission:2342341243]And you didn't specify which problem is this.
Anyway, two problems with your code:
1 if n is even you first need to check whether a solution exist or not. this step comes after that.
2 this is slow imagine l =
1000000000000000000000000000001i.e. $$$(2^{30} + 1)$$$your for loop will run $$$2^{30}$$$ times i.e. about 1 billion operations.
better way to do so is
for (i = 1; i < l; i * = 2)Is there a problem with the and operator or is it fine? this is the link of the problem: https://mirror.codeforces.com/contest/2119/problem/C
D was a really cool problem. It took a lot of time to understand, but it was definitely worth it, a very well-crafted and insightful problem.
For Problem C, can somebody explain why the tactic that a1 exor a2 exor .... an-2 = l doesn't work then why aren't we checking the same thing for l+1,l+2, ... r?
Mai karu explain?
yess bhai
Can somebody explain why does $$$s_1 \leq \frac{S - m}{2} + \frac{\max(a_i)}{2} \leq m + s_2$$$ hold in B?
C was a good problem! I was able to guess the solution instantly but proving was satisfying.
That shows why i love Codeforces. It has a difficult thinking but easy coding. Thanks..
I received a message saying my solution 327573503 for problem 2119A coincided with others (mokshitha_156). I admit that I shared my code with a friend because she was struggling, and I didn't fully understand the consequences.
I now realize this is a violation of Codeforces rules. I sincerely apologize and accept disqualification from the round. This was my first and last mistake.I will never repeat it.
Please consider this as a one‑time error. Thank you for understanding.
Hi, I'm appealing the flag on solution 327610026 (problem 2119C). I regularly use Ideone without knowing that the default setting makes code public, which unintentionally allowed others to see and copy it. I didn’t share links or collaborate—I simply didn't realize I needed to manually switch it to private. Today after getting an alert, i realised about this feature... I sincerely apologize for the unintentional leak. This was not deliberate plagiarism.
Hello Codeforces team,
I have received the notification that my solution for Problem 2119A coincided with other submissions in this round.
I want to clarify that I did not intentionally share my code with anyone, nor did I copy it from anyone else. I understand that my solution matching others so closely may still be considered a violation of the rules.
I fully respect the rules of fair competition, and I apologize for any inconvenience caused. I will make sure to be more careful in the future to avoid any similar issues.
Thank you for your understanding and for your hard work organizing these contests.
[deleted]
Dear Codeforces team,
I am appealing the skipped verdicts on my submissions for both Problem A (Add or Xor) and Problem B (Line Segment) in Round 1035 (Div. 2). I want to clearly state that I did not cheat, collaborate, or share my code with anyone. Below is a detailed explanation of my approaches and supporting context:
Problem A (2119A – Add or Xor): The task is to transform a to b using +1 (cost x) and XOR with 1 (cost y) at minimum cost. I modeled this as a shortest path problem, where each integer is a node and operations define the edges with associated costs. This directly maps to Dijkstra's algorithm , I used my own Dijkstra template, which I regularly use in contests.
My code uses a priority_queue and visited-distance tracking, and I limited the state space to a fixed size. I now understand that many others independently arrived at a similar Dijkstra solution, which likely caused this mass flagging. But the logic and template used are part of my personal style, not copied from any contestant.
Problem B (2119B – Line Segment): My approach for this problem was if the actual dist is in between the max dist from the source and min dist from the source then we can reach the terminal point so It would say Yes else No.
This approach was derived independently during the contest . It's a clean mathematical solution and not something I copied. And my code style is always the same .
I kindly request a manual review of my submissions. I understand the difficulty of handling mass similarity cases, but I hope my explanation clarifies that I competed honestly, with my own logic and coding style.
Submission IDs:
A:327563324
B:327606925
I am new to Codeforces and was not fully aware of the rules regarding code sharing and plagiarism. In a recent contest, I mistakenly copied a solution from Ideone without understanding that this violated the Codeforces regulations. I now realize this was a serious mistake.
I sincerely apologize for this action. It was not done with any malicious intent, but rather out of ignorance. I assure you that I have learned from this experience and will never repeat such behavior again. I respectfully request your understanding and a chance to continue participating on the platform while following all the rules properly from now on.
Thank you for your time and consideration.
oh... so it was you cheater... who took my code...
Есть ли у задачи B более простое решение? Или можно ли упростить авторское решение?
На точке Q проводится окружность с радиусом r, равным наибольшему элементу массива a, и проверяется, может ли точка P коснуться этой окружности. В зависимости от расстояния d между точками P и Q (d ≥ r или d < r), знак выражения меняется.
For 2119D - Token Removing, here's my solution summary with visual explanation https://www.youtube.com/live/O0qbgoDUj0g?t=1962s
I have a feeling like nowadays the problem in the round have a trends going observation heavy,i don't think observation in B is popular,i would doubt that is the author put the thing he learn on school math class to the problem?Just like I put a integration by part calculus in the problem then say from some observation we can use defination of integration to prove the formula.For C,D i think is good,nice logic light observation and at least can learn something.
Hello Codeforces team,
I have received the notification that my solution 327604665 for Problem 2119C coincided with other submissions in this round. I would like to tell that you can check my previous submission as well. when i got to know that it is causing TLE then i optimized it using this approach and took hints from google as well. The entire logic was mine. Hopefully will not happen next time.
Really liked problem D and F.
Good questions!
can someone tell that in D why does counting the number of sequences work?because we were asked for number of ways right?
I see A B C D all four problems are interesting and challenging as well and qsn D takes more than four hours to understand the eaxactly the solution after attempting.
In problem F, one can compensate for the inability to make a few observations by bashing with an ugly centroid decomposition
what?
there's a solution with centroid decomposition that's easier to think of but much harder to implement
The definition of $$$f(i, j)$$$ in the editorial of D is very weird to me. Isn't it more intuitive to define $$$i$$$ as the length of the token sequence and $$$j$$$ as the lower bound of the last token index? I get that the author's solution is perfectly valid, but it is weird to me that $$$i-j$$$ represents the length of the token sequence.