A NJU emulator
题意:在一个最基础的堆栈式计算机中,只允许对栈顶元素进行修改。用以下几种操作达成给定的数 NNN,N≤264N \leq 2^{64}N≤264。要求步骤在 505050 步以内:
- 给栈顶元素加上栈中的某一个元素。
- 给栈顶元素减去栈中的某一个元素。
- 给栈顶元素乘以栈种的某一个元素。
- 复制栈顶元素。
- 弹出栈顶元素。
- 向栈中添加数字 111,放在栈顶。
- 交换栈顶元素与次栈顶元素。
- 输出栈顶元素,停机。(必须有此操作)
解法:首先考虑一个步数稍微多一点的操作。模拟快速幂的过程,如果是偶数就除以 222 ,否则减 111。对应于操作,即是:加一则 p1
,add 1
;乘以二则是 dup
、add 1
。这样操作次数仅 2⌊log2N⌋+12 \lfloor \log_2 N \rfloor+12⌊log2N⌋+1 次。但是鉴于此题的要求,这样的操作步数仍然有 130130130 次,无法接受。
那么这启发了我们。在上面的操作中,我们只利用了加法,连减法和乘法都没有用到,这一定是不够好的。这样的操作本质是不断的 ×2\times 2×2 或者 +1+1+1,类似于二进制操作。那么更一般的,可否 ×k\times k×k 或者 +p+p+p 呢?在快速幂中,我们不便于直接 ×k\times k×k,但是这里我们可以直接进行乘法操作,因而考虑利用好乘法运算。
基于这样的想法,我们考虑每次除以 kkk,然后补足余数。除以 kkk 的操作可以递归的进行,结束后直接乘以 kkk;余数只有 [0,k−1][0,k-1][0,k−1],因而预处理出 [0,k][0,k][0,k] 入栈中即可。如果直接暴力的计算,每次 dup
、add 1
,需要 2k2k2k 次操作,加上类快速幂的 2⌊logkN⌋2\left \lfloor \log_k N \right \rfloor2⌊logkN⌋,选取一个合适的 kkk,如 161616,则总次数可以降低到 606060 次左右。
进一步的优化它——考虑 [0,k][0,k][0,k] 中的数是否需要都算出来。容易发现,如果有了 kkk 与 [1,⌊k2⌋]\displaystyle \left[1,\left \lfloor \frac{k}{2} \right \rfloor\right ][1,⌊2k⌋],则剩下的数不需要计算出来,只需要直接用 kkk 去减即可。而在减去这里,我们不需要一定累加一个 kkk,然后再减去对应的部分,只需要将其融入到快速幂中,让其自动的多乘出来一个 kkk,就可以再减少一次。
选定 k=16k=16k=16,使用这一方法总的步骤不超过 323232 次。
#include <cstdio>
#include <algorithm>
using namespace std;
const int place[17] = {0, 9, 8, 7, 6, 5, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1};//数字对应的栈中的位置
void print(unsigned long long x)
{
if(x<=8)
{
printf("p1\n");
if (x > 1)
printf("add %d\n", place[x - 1]);
return;
}
else if(x<16)
{
printf("dup\n");
printf("sub %d\n", place[16 - x]);
return;
}
else
{
int t = x & 15;
if(t<=8)
{
print(x >> 4);
printf("mul %d\n", place[16]);
if(t)
printf("add %d\n", place[t]);
}
else//余数大于8,则需要减去多余部分。让快速幂中多乘出来一个即可。
{
print((x >> 4) + 1);
printf("mul %d\n", place[16]);
printf("sub %d\n", place[16 - t]);
}
return;
}
}
int main()
{
int t;
unsigned long long x;
scanf("%d", &t);
while(t--)
{
scanf("%lld", &x);
if(x==0)
{
printf("p1\n");
printf("sub 0\n");
printf("end\n");
continue;
}
printf("p1\n");
for (int i = 1; i <= 7;i++)
{
printf("dup\n");
printf("add %d\n", i);
}
printf("dup\n");
printf("add 0\n");
//预处理出1~8,16放入栈中。place中存放了其对应的访问位置
print(x);
printf("end\n");
}
return 0;
}
B Just another board game
题意:有一个 n×mn \times mn×m 的棋盘,每个点有一个点权,甲乙二人轮流移动棋子,初始时位于 (1,1)(1,1)(1,1) 处。甲只能横向移动棋子,而乙只能纵向移动棋子。进行 kkk 轮后游戏结束,二人都可以中途叫停游戏。甲希望棋子移动到的位置点权最大,而乙希望最小。问最终棋子移动到的位置的点权。
解法:如果现在游戏仅剩一轮,那么走这一步的人一定是随其心意,直接贪心的找到那一行最大的(或者那一列最小的)点即可。考虑倒着进行这一游戏。基于这一想法,那么倒数第二个人就要选择行最大值最小的一行(或者列最小值最大的一列),这样才能尽可能的阻拦对手。重复这一操作,那么这一猜疑链就将持续下去——二人的每一步操作都只是在让对手下一步难走,除了仅剩一步这一操作。
考虑中途停止的问题。这个决策并不会影响最终结果。其原因在于——一个人如果要中途喊停,则必须是他移动完之后给对手留下的局面太好,使得他自己的结果不利(注意到二人对结果评价标准完全相反)。但是显然,他可以选择一个让对手最不利的局面去走。如果他现在真的要喊停,那么就证明怎么移动,对于对手来说,结局都只会比现在好。考虑这种情况的发生,只有可能是在满足最大值最小的一行(或者最小值最大的一列)上。因而,喊停的条件与直接走的条件是吻合的。
所以基于这一猜疑链,我们可以得到这一结论——
- 只剩一轮——那么甲直接走到第一行最大的地方。
- 偶数轮——那么走到最小值最大的一列,取其最小值。
- 奇数轮——那么走到最大值最小的一行,取其最大值。
注意一个特殊情况——甲可以在第一轮喊停,如果这样的结果比 (1,1)(1,1)(1,1) 的结果坏。
#include <cstdio>
#include <algorithm>
#include <vector>
#include <assert.h>
using namespace std;
const int inf = 0x3f3f3f3fll;
const int N = 100000;
int main()
{
int t, n, m;
long long k;
scanf("%d", &t);
while(t--)
{
scanf("%d%d%lld", &n, &m, &k);
vector<vector<int>> num(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n;i++)
for (int j = 1; j <= m;j++)
scanf("%d", &num[i][j]);
if(k==1)
{
int maximum = -1;
for (int i = 1; i <= m;i++)
maximum = max(maximum, num[1][i]);
printf("%d\n", maximum);
}
else if(k&1)
{
int minimum = inf;
for (int i = 1; i <= n;i++)
{
int maximum = -1;
for (int j = 1; j <= m;j++)
maximum = max(maximum, num[i][j]);
minimum = min(minimum, maximum);
}
printf("%d\n", max(num[1][1], minimum));
}
else
{
int maximum = 0;
for (int j = 1; j <= m;j++)
{
int minimum = inf;
for (int i = 1; i <= n;i++)
minimum = min(minimum, num[i][j]);
maximum = max(maximum, minimum);
}
printf("%d\n", max(num[1][1], maximum));
}
}
return 0;
}
C Dota2 Pro Circuit
题意:有 nnn 个队伍,每个队初始有一个分数 aia_iai,之后有一场锦标赛,得第 iii 名可以获得 bib_ibi 分。问每一支队伍最好排名与最差排名。n≤5×103n \leq 5 \times 10^3n≤5×103。
解法:一个贪心的想法——最好排名给 b1b_1b1 分,最差排名给 bnb_nbn 分。关键是剩余怎么选分数。
对于最好排名,首先分数原本就比他严格低的,一定没希望比他高了。现在主要是让分数原本比他高的现在比他低。从原本的最高分开始,尽可能的打低分,如果现在未被占用的最低分无法满足比他低那么直接放弃掉,不要让他占用了最低分。
最坏排名类似,但是注意并列的情况。如果给分并列了,排名也要上升一位——但是这相对于让这个人比他低,并列还是更优一些的。
整体复杂度 O(n2)O(n^2)O(n2)。
#include <cstdio>
#include <algorithm>
#include <memory.h>
using namespace std;
const int N = 5000;
long long b[N + 5];
struct node
{
long long num;
int id;
bool operator <(const node &b)const
{
return num < b.num;
}
};
struct node a[N + 5];
int ans[2][N + 5];
int main()
{
int n, t;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for (int i = 1; i <= n;i++)
{
scanf("%lld", &a[i].num);
a[i].id = i;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
scanf("%lld", &b[i]);
for (int i = 1; i <= n;i++)
{
int place = n, rank = n - i + 1;
for (int j = n; j > i; j--)
if (a[j].num + b[place] <= a[i].num + b[1])
{
place--;
rank--;
}
ans[0][a[i].id] = rank;
}
for (int i = 1; i <= n; i++)
{
int place = 1, rank = n - i + 1;
for (int j = 1; j < i; j++)
if (a[j].num + b[place] > a[i].num + b[n])
{
place++;
rank++;
}
for (int j = i + 1; j <= n; j++)
if (a[j].num + b[place] == a[i].num + b[n])
{
place++;
rank--;
}
ans[1][a[i].id] = rank;
}
for (int i = 1; i <= n; i++)
printf("%d %d\n", ans[0][i], ans[1][i]);
}
return 0;
}
F Guess the weight
题意:给定一个数列,每个数的范围均在 [1,N][1,N][1,N] 之中,N=2×105N = 2\times 10^5N=2×105。执行 qqq 次以下两个操作:
- 向数列中插入 xxx 个数字 www。满足 w∈[1,N]w \in [1,N]w∈[1,N]。
- 询问 ∑i=1Ncntimax(∑j=1i−1cntj,∑j=i+1Ncntj)\displaystyle \sum_{i=1}^{N} cnt_i\max\left(\sum_{j=1}^{i-1} cnt_j,\sum_{j=i+1}^{N} cnt_j\right)i=1∑Ncntimax(j=1∑i−1cntj,j=i+1∑Ncntj)。其中 cntjcnt_jcntj 为数字 jjj 在整个数列中出现的次数。
满足 q≤2×105q \leq 2\times 10^5q≤2×105。
解法:记 f(x)=max(∑j=1x−1cntj,∑j=x+1Ncntj)\displaystyle f(x)=\max\left(\sum_{j=1}^{x-1} cnt_j,\sum_{j=x+1}^{N} cnt_j\right)f(x)=max(j=1∑x−1cntj,j=x+1∑Ncntj)。不难发现,存在一个分界点 ddd,当 x≥dx \geq dx≥d 时,f(x)=∑j=1x−1cntj\displaystyle f(x)=\sum_{j=1}^{x-1} cnt_jf(x)=j=1∑x−1cntj,而 x>dx>dx>d 时则恒为 f(x)=∑j=x+1Ncntj\displaystyle f(x)=\sum_{j=x+1}^{N} cnt_jf(x)=j=x+1∑Ncntj。
这个式子可以直接使用线段树维护。用一个线段树维护 ∑k=lrcntk∑j=1kcntj,∑k=lrcntk∑j=k+1Ncntj\displaystyle \sum_{k=l}^r cnt_k \sum_{j=1}^{k} cnt_j,\sum_{k=l}^r cnt_k \sum_{j=k+1}^N cnt_jk=l∑rcntkj=1∑kcntj,k=l∑rcntkj=k+1∑Ncntj,用一个树状数组维护前缀和 ∑j=1icntj\displaystyle \sum_{j=1}^{i} cnt_jj=1∑icntj,每次询问时利用树状数组二分出分界点 ddd,然后线段树查询 [d+1,N][d+1,N][d+1,N] 上 ∑k=1dcntk∑j=1kcntj\displaystyle \sum_{k=1}^d cnt_k \sum_{j=1}^k cnt_jk=1∑dcntkj=1∑kcntj,[1,d][1,d][1,d] 上 ∑k=d+1Ncntk∑j=k+1Ncntj\displaystyle \sum_{k=d+1}^N cnt_k \sum_{j=k+1}^N cnt_jk=d+1∑Ncntkj=k+1∑Ncntj。整体复杂度 O(qlog2n)O(q \log^2n)O(qlog2n)。
但是这样是过于繁琐的,考虑能否化简这一式子。记 Si=∑j=1icntj\displaystyle S_i=\sum_{j=1}^i cnt_jSi=j=1∑icntj,分界点为 ddd,整个数列长度为 SN=nS_N=nSN=n,原式可化为 ∑i=1dcnti(n−Si)+∑i=d+1NcntiSi−1\displaystyle \sum_{i=1}^d cnt_i (n-S_i)+\sum_{i=d+1}^N cnt_iS_{i-1}i=1∑dcnti(n−Si)+i=d+1∑NcntiSi−1。注意到 ∑i=1dcnti×n=nSd\displaystyle \sum_{i=1}^d cnt_i \times n=nS_di=1∑dcnti×n=nSd,∑i=1dcntiSi=∑i=1dcntiSi−1+∑i=1dcnti2\displaystyle \sum_{i=1}^d cnt_iS_i=\sum_{i=1}^d cnt_iS_{i-1}+\sum_{i=1}^d cnt_i^2i=1∑dcntiSi=i=1∑dcntiSi−1+i=1∑dcnti2后,面这一式子的变形是为了和式子后半的形式匹配。这样就可以将原式变为 nSd+∑i=1NcntiSi−1−(2∑i=1dcntiSi−1+∑i=1dcnti2)\displaystyle nS_d+\sum_{i=1}^N cnt_iS_{i-1}-\left(2\sum_{i=1}^d cnt_iS_{i-1}+\sum_{i=1}^d cnt_i^2\right )nSd+i=1∑NcntiSi−1−(2i=1∑dcntiSi−1+i=1∑dcnti2)。
考察 ∑i=1dcntiSi−1\displaystyle \sum_{i=1}^d cnt_i S_{i-1}i=1∑dcntiSi−1 的性质。显然,∫lrf(x)(∫lrf(x)dx)dx=12(∫lrf(x)dx)2\displaystyle \int_{l}^r f(x) \left(\int_{l}^r f(x) {\rm d}x\right) {\rm d} x=\frac{1}{2}\left(\int_{l}^r f(x) {\rm d}x\right)^2∫lrf(x)(∫lrf(x)dx)dx=21(∫lrf(x)dx)2,那么对应于其离散形式,不难得到,∑i=1dcntiSi=Sd22\displaystyle \sum_{i=1}^d cnt_i S_i=\frac{S_d^2}{2}i=1∑dcntiSi=2Sd2,因而 ∑i=1dcntiSi−1=∑i=1dcnti(Si−cnti)=Sd22−∑i=1dcnti2\displaystyle \sum_{i=1}^d cnt_i S_{i-1}=\sum_{i=1}^dcnt_i(S_i-cnt_i)=\frac{S_d^2}{2}-\sum_{i=1}^d cnt_i^2i=1∑dcntiSi−1=i=1∑dcnti(Si−cnti)=2Sd2−i=1∑dcnti2。因而,原式可以继续化简为 nSd+12(SN2−∑i=1Ncnti2)−Sd2\displaystyle nS_d +\frac{1}{2} \left(S_N^2-\sum_{i=1}^N cnt_i^2\right)-S_d^2nSd+21(SN2−i=1∑Ncnti2)−Sd2。那么这个式子只需要维护前缀和与整体的平方和即可。借助树状数组进行二分可以得到复杂度为 O(qlog2n)O(q \log^2n)O(qlog2n)。
#include <cstdio>
#include <algorithm>
using namespace std;
long long gcd(long long x,long long y)
{
return y == 0 ? x : gcd(y, x % y);
}
const int N = 200000;
long long t[N + 5];
int lowbit(int x)
{
return x & (-x);
}
void update(int x,long long k)
{
while (x <= N)
{
t[x] += k;
x += lowbit(x);
}
return;
}
long long query(int x)
{
long long ans = 0;
while(x)
{
ans += t[x];
x -= lowbit(x);
}
return ans;
}
long long cnt[N + 5];
int main()
{
int T, n, q, x;
scanf("%d", &T);
while(T--)
{
long long squ = 0, tot = 0;
for (int i = 1; i <= N;i++)
cnt[i] = t[i] = 0;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n;i++)
{
scanf("%d", &x);
cnt[x]++;
update(x, 1);
}
tot = n;
for (int i = 1; i <= N;i++)
squ += cnt[i] * cnt[i];
while (q--)
{
int op, x, w;
scanf("%d", &op);
if (op == 2)
{
int left = 1, right = N, div = 0;
while(left<=right)
{
int mid = (left + right) >> 1;
if (2 * query(mid - 1) <= tot - cnt[mid])
{
div = mid;
left = mid + 1;
}
else
right = mid - 1;
}
long long down = tot * (tot - 1);
long long sump = query(div);
long long up = (tot * tot - squ) / 2 + tot * sump - sump * sump;
long long d = gcd(up, down);
printf("%lld/%lld\n", up / d, down / d);
}
else
{
scanf("%d%d", &x, &w);
update(w, x);
tot += x;
squ -= cnt[w] * cnt[w];
cnt[w] += x;
squ += cnt[w] * cnt[w];
}
}
}
return 0;
}
G Boring data structure problem
题意:有一个空的双端队列,依次插入正整数。可以从前插,也可以从后插。还有删除操作,删除数字为 xxx 的数。询问其在最中间的数(⌈m+12⌉\displaystyle \left\lceil \frac{m+1}{2} \right\rceil⌈2m+1⌉)是多少。
解法:考虑用链表实现。每次插入与删除只会涉及一个数,因而中位数也至多移动一位。
- 从前方插入——如果原本为奇数个,则中位数不动;反之则要前移一位。
- 从后方插入——如果原本为偶数个,则中位数不动;反之则要后移一位。
- 删除一个数——如果删除位置在前方,则视原链表长度,若为奇则后移;对称的,删除位置在后方,原本长度为偶数则前移一位。如果删除的刚好是中位数,则一定要移动,奇数则后移,偶数则前移。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 10000000;
char op[5];
int former[2 * N + 5], latter[2 * N + 5], num[2 * N + 5], list[2 * N + 5];
int id[2 * N + 5];
int main()
{
int q;
scanf("%d", &q);
int tot = 0, left = 0, right = 0, mid = 0, siz = 0, place = 0;
int lefthead = 0, righthead = 0;
while (q--)
{
scanf("%s", op);
int x;
switch (op[0])
{
case 'L':
{
tot++;
id[tot] = left--;
place++;
list[place] = tot;
num[tot] = place;
if (lefthead)
{
former[place] = place;
latter[place] = lefthead;
former[lefthead] = place;
lefthead = place;
}
else
{
lefthead = righthead = place;
latter[place] = former[place] = place;
}
siz++;
if (siz == 1)
mid = place;
else if (siz % 2 == 1)
mid = former[mid];
break;
}
case 'R':
{
tot++;
id[tot] = ++right;
place++;
list[place] = tot;
num[tot] = place;
if (righthead)
{
latter[place] = place;
former[place] = righthead;
latter[righthead] = place;
righthead = place;
}
else
{
lefthead = righthead = place;
former[place] = latter[place] = place;
}
siz++;
if (siz == 1)
mid = place;
else if (siz % 2 == 0)
mid = latter[mid];
break;
}
case 'G':
{
scanf("%d", &x);
siz--;
if (!siz)
{
lefthead = righthead = 0;
break;
}
int midplace = id[list[mid]];
int nowplace = id[x];
if(num[x]==lefthead)
lefthead = latter[lefthead];
if(num[x]==righthead)
righthead = former[righthead];
latter[former[num[x]]] = latter[num[x]];
former[latter[num[x]]] = former[num[x]];
if (midplace < nowplace)
{
if (siz % 2 == 1)
mid = former[mid];
}
else if (midplace > nowplace)
{
if (siz % 2 == 0)
mid = latter[mid];
}
else
{
if (siz % 2 == 0)
mid = latter[mid];
else
mid = former[mid];
}
break;
}
case 'Q':
{
printf("%d\n", list[mid]);
break;
}
}
}
return 0;
}
J Unfair contest
题意:一场比赛有 nnn 个评委给两个选手打分,要去掉 sss 个最高分与 ttt 个最低分。现在给出了前 n−1n-1n−1 个评委的分数 a[1,n−1],b[1,n−1]a[1, n-1],b[1, n-1]a[1,n−1],b[1,n−1],现在作为第 nnn 个评委,给分区间在 [1,h][1,h][1,h] 内,要求让第一个人一定获胜,并且给第一个人的分数减第二个人的分数尽可能的小,问最小值为多少。0≤s,t≤n−10 \leq s,t \leq n-10≤s,t≤n−1,s+t≤n−1s+t \leq n-1s+t≤n−1。
解法:先划除最高的 s−1s-1s−1 与最低的 t−1t-1t−1 个分数,那么 a[n]a[n]a[n] 的区间就划分成三段——低于 a[t]a[t]a[t],高于 a[s]a[s]a[s],与居中,b[n]b[n]b[n] 同理。首先是最基础的情况——如果 a[n]a[n]a[n] 给最高(大于 a[s]a[s]a[s] 就会被划去),而 b[n]b[n]b[n] 给最低(小于 b[t]b[t]b[t] 也会被划去)都还无法反超,那么只有无解;反过来,a[n]a[n]a[n] 最低而 b[n]b[n]b[n] 最高也不会被反超,那么答案为 1−h1-h1−h——给第一个人最低分而第二个人最高分。
考虑一般情况。记二人尚未被划除的分数之差为 difdifdif,最朴素的答案就是 1−dif1-dif1−dif——刚好反超一分。考虑到分的情况太多,那么直接考虑怎么样的答案会使得更小。容易发现只有两种情况——a[n]≤a[t]a[n] \leq a[t]a[n]≤a[t] 而 b[t]≤b[n]≤b[s]b[t] \leq b[n] \leq b[s]b[t]≤b[n]≤b[s],与 a[s]≤a[n]≤a[t]a[s] \leq a[n] \leq a[t]a[s]≤a[n]≤a[t] 而 b[s]≤b[n]b[s] \leq b[n]b[s]≤b[n]。这是因为,在第一种情况下,a[n]a[n]a[n] 可以直接取 111,b[n]b[n]b[n] 根据 difdifdif 来选定,由于 dif+a[t]−b[n]≥1dif+a[t]-b[n] \geq 1dif+a[t]−b[n]≥1,b[n]≤dif+a[s]−1b[n] \leq dif+a[s]-1b[n]≤dif+a[s]−1,最后的答案为 2−dif−a[s]2-dif-a[s]2−dif−a[s],可能比 1−dif1-dif1−dif 小;在第二种情况下,b[n]b[n]b[n] 拉满取 hhh,a[n]a[n]a[n] 按照要求选定,有 dif+a[n]−b[s]≥1dif+a[n]-b[s] \geq 1dif+a[n]−b[s]≥1,因而 a[n]≥1+b[s]−difa[n] \geq 1+b[s]-difa[n]≥1+b[s]−dif,这样的答案为 1+b[s]−dif−h1+b[s]-dif-h1+b[s]−dif−h,均可能比 1−dif1-dif1−dif 小。
如果 s,ts,ts,t 为 000 则预支一个 000 分或者 hhh 分。可以证明这样的答案不会影响。
总复杂度 O(nlogn)O(n \log n)O(nlogn)。
#include <cstdio>
#include <algorithm>
using namespace std;
const long long inf = 0x3f3f3f3f3f3f3f3fll;
const int N = 100000;
long long a[N + 5], b[N + 5];
int main()
{
int T, n, s, t;
long long h;
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d%lld", &n, &s, &t, &h);
s = n - s;
for (int i = 1; i < n; i++)
scanf("%lld", &a[i]);
sort(a + 1, a + n);
a[0] = 1;
a[n] = h;
for (int i = 1; i < n;i++)
scanf("%lld", &b[i]);
sort(b + 1, b + n);
b[0] = 1;
b[n] = h;
long long dif = 0;
for (int i = t + 1; i < s; i++)
dif += a[i] - b[i];
if (dif + a[s] - b[t] <= 0)
printf("IMPOSSIBLE\n");
else if (dif + a[t] - b[s] >= 1)
printf("%lld\n", 1 - h);
else
{
long long ans = 1 - dif;
//a[n]<=a[t] && b[t]<=b[n]<=b[s]
if (dif + a[t] - b[t] >= 1)//here b[n]>=b[t]
//a[n]=1,b[n]=dif-1+a[t]
ans = min(ans, 2 - dif - a[t]);
if (dif + a[s] - b[s] >= 1)//here a[t]<=a[n]<=a[s] && b[n]>=b[t]
//b[n]=h,a[n]+dif-b[s]>=1,a[n]=b[s]-dif+1
ans = min(ans, 1 - dif + b[s] - h);
printf("%lld\n", ans);
}
}
return 0;
}