杭电多校第九场8月17日补题记录

本文探讨了如何运用快速幂算法和动态规划策略解决计算问题,包括堆栈操作优化、棋盘游戏策略分析、数列求和优化及比赛评分计算等。通过巧妙地转换和优化问题,实现了复杂度的降低,有效解决了各种计算难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A NJU emulator

题意:在一个最基础的堆栈式计算机中,只允许对栈顶元素进行修改。用以下几种操作达成给定的数 NNNN≤264N \leq 2^{64}N264。要求步骤在 505050 步以内:

  1. 给栈顶元素加上栈中的某一个元素。
  2. 给栈顶元素减去栈中的某一个元素。
  3. 给栈顶元素乘以栈种的某一个元素。
  4. 复制栈顶元素。
  5. 弹出栈顶元素。
  6. 向栈中添加数字 111,放在栈顶。
  7. 交换栈顶元素与次栈顶元素。
  8. 输出栈顶元素,停机。(必须有此操作)

解法:首先考虑一个步数稍微多一点的操作。模拟快速幂的过程,如果是偶数就除以 222 ,否则减 111。对应于操作,即是:加一则 p1add 1;乘以二则是 dupadd 1。这样操作次数仅 2⌊log⁡2N⌋+12 \lfloor \log_2 N \rfloor+12log2N+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,k1],因而预处理出 [0,k][0,k][0,k] 入栈中即可。如果直接暴力的计算,每次 dupadd 1,需要 2k2k2k 次操作,加上类快速幂的 2⌊log⁡kN⌋2\left \lfloor \log_k N \right \rfloor2logkN,选取一个合适的 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. 只剩一轮——那么甲直接走到第一行最大的地方。
  2. 偶数轮——那么走到最小值最大的一列,取其最小值。
  3. 奇数轮——那么走到最大值最小的一行,取其最大值。

注意一个特殊情况——甲可以在第一轮喊停,如果这样的结果比 (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^3n5×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 次以下两个操作:

  1. 向数列中插入 xxx 个数字 www。满足 w∈[1,N]w \in [1,N]w[1,N]
  2. 询问 ∑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=1Ncntimax(j=1i1cntj,j=i+1Ncntj)。其中 cntjcnt_jcntj 为数字 jjj 在整个数列中出现的次数。

满足 q≤2×105q \leq 2\times 10^5q2×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=1x1cntj,j=x+1Ncntj)。不难发现,存在一个分界点 ddd,当 x≥dx \geq dxd 时,f(x)=∑j=1x−1cntj\displaystyle f(x)=\sum_{j=1}^{x-1} cnt_jf(x)=j=1x1cntj,而 x>dx>dx>d 时则恒为 f(x)=∑j=x+1Ncntj\displaystyle f(x)=\sum_{j=x+1}^{N} cnt_jf(x)=j=x+1Ncntj

这个式子可以直接使用线段树维护。用一个线段树维护 ∑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=lrcntkj=1kcntj,k=lrcntkj=k+1Ncntj,用一个树状数组维护前缀和 ∑j=1icntj\displaystyle \sum_{j=1}^{i} cnt_jj=1icntj,每次询问时利用树状数组二分出分界点 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=1dcntkj=1kcntj[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+1Ncntkj=k+1Ncntj。整体复杂度 O(qlog⁡2n)O(q \log^2n)O(qlog2n)

但是这样是过于繁琐的,考虑能否化简这一式子。记 Si=∑j=1icntj\displaystyle S_i=\sum_{j=1}^i cnt_jSi=j=1icntj,分界点为 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=1dcnti(nSi)+i=d+1NcntiSi1。注意到 ∑i=1dcnti×n=nSd\displaystyle \sum_{i=1}^d cnt_i \times n=nS_di=1dcnti×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=1dcntiSi=i=1dcntiSi1+i=1dcnti2后,面这一式子的变形是为了和式子后半的形式匹配。这样就可以将原式变为 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=1NcntiSi1(2i=1dcntiSi1+i=1dcnti2)

考察 ∑i=1dcntiSi−1\displaystyle \sum_{i=1}^d cnt_i S_{i-1}i=1dcntiSi1 的性质。显然,∫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)^2lrf(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=1dcntiSi=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=1dcntiSi1=i=1dcnti(Sicnti)=2Sd2i=1dcnti2。因而,原式可以继续化简为 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(SN2i=1Ncnti2)Sd2。那么这个式子只需要维护前缀和与整体的平方和即可。借助树状数组进行二分可以得到复杂度为 O(qlog⁡2n)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\rceil2m+1)是多少。

解法:考虑用链表实现。每次插入与删除只会涉及一个数,因而中位数也至多移动一位。

  1. 从前方插入——如果原本为奇数个,则中位数不动;反之则要前移一位。
  2. 从后方插入——如果原本为偶数个,则中位数不动;反之则要后移一位。
  3. 删除一个数——如果删除位置在前方,则视原链表长度,若为奇则后移;对称的,删除位置在后方,原本长度为偶数则前移一位。如果删除的刚好是中位数,则一定要移动,奇数则后移,偶数则前移。
#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-1n1 个评委的分数 a[1,n−1],b[1,n−1]a[1, n-1],b[1, n-1]a[1,n1],b[1,n1],现在作为第 nnn 个评委,给分区间在 [1,h][1,h][1,h] 内,要求让第一个人一定获胜,并且给第一个人的分数减第二个人的分数尽可能的小,问最小值为多少。0≤s,t≤n−10 \leq s,t \leq n-10s,tn1s+t≤n−1s+t \leq n-1s+tn1

解法:先划除最高的 s−1s-1s1 与最低的 t−1t-1t1 个分数,那么 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-h1h——给第一个人最低分而第二个人最高分。

考虑一般情况。记二人尚未被划除的分数之差为 difdifdif,最朴素的答案就是 1−dif1-dif1dif——刚好反超一分。考虑到分的情况太多,那么直接考虑怎么样的答案会使得更小。容易发现只有两种情况——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] 可以直接取 111b[n]b[n]b[n] 根据 difdifdif 来选定,由于 dif+a[t]−b[n]≥1dif+a[t]-b[n] \geq 1dif+a[t]b[n]1b[n]≤dif+a[s]−1b[n] \leq dif+a[s]-1b[n]dif+a[s]1,最后的答案为 2−dif−a[s]2-dif-a[s]2difa[s],可能比 1−dif1-dif1dif 小;在第二种情况下,b[n]b[n]b[n] 拉满取 hhha[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]difh,均可能比 1−dif1-dif1dif 小。

如果 s,ts,ts,t000 则预支一个 000 分或者 hhh 分。可以证明这样的答案不会影响。

总复杂度 O(nlog⁡n)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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值