张经理的员工(2020西工大校赛 树状数组、预处理前缀和)

张经理的员工

题目链接
2020年西北工业大学“编程之星”程序设计挑战赛(大学生程序设计创新实践基地队员春季选拔赛)

题目描述
张经理的公司的办公室长达100000米,从最左端开始每间隔1米都有一个工位(从第1米开始有工位),位于第 i i i 米的工位称为 i i i 号工位,且这些工位都在一条水平线上。他有 n n n 个员工,每个员工分别位于 x i x_i xi 号工位上(不同员工可能位于同一个工位)。
现在张经理想把员工聚集在某两个工位上,他有 q q q 套方案(每套方案包含两个工位号,两个工位号可能相同),他想知道对于每套方案,所有员工都到达两个工位中的某一个所需走的最短路径之和是多少。
输入描述:
第一行输入两个正整数 n , q n, q n,q
第二行输入 n n n 个正整数 x i x_i xi ,分别代表第 i i i 个员工的工位
之后 q q q 行每行输入两个整数 a , b a,b ab ,代表该套方案要求的两个集合位置
( 1 < = n , q , x i , a , b < = 1 0 5 ) (1<=n,q,xi,a,b<=10^5) (1<=n,q,xi,a,b<=105)
输出描述:
对于每套方案,输出一个整数代表答案,每个答案独占一行。
示例1
输入
3 2
1 3 5
1 4
2 1
输出
2
4

思路:
聚集到 a , b a,b a,b 两个位置,分成4部分

  1. a a a 之前的那部分移动到 a a a 位置( a a a 位置有没有人对计算不影响)
  2. b b b 之后的那部分移动到 b b b 位置
  3. m i d = ( a + b ) / 2 mid=(a+b)/2 mid=(a+b)/2 (整除), a a a m i d mid mid 之间的数移动到 a a a 位置
  4. m i d + 1 mid+1 mid+1 b b b 之间的数移动到 a a a 位置

Code:

预处理位置坐标的前缀和

#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, q, l, r, x, pos[N];
ll cnt[N], sum[N];
int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> x;
        pos[x]++;
    }
    for (int i = 1; i <= 100000; i++)
    {
        cnt[i] = cnt[i - 1] + pos[i] * 1LL;
        sum[i] = sum[i - 1] + pos[i] * i * 1LL;
    }
    while (q--)
    {
        cin >> l >> r;
        if (l > r)
            swap(l, r);
        int mid = (l + r) >> 1;
        ll ans = 0;
        ans += l * cnt[l] - sum[l];                                       // L前
        ans += sum[100000] - sum[r - 1] - (cnt[100000] - cnt[r - 1]) * r; // R后
        ans += sum[mid] - sum[l] - (cnt[mid] - cnt[l]) * l;               // L <- mid
        ans += r * (cnt[r - 1] - cnt[mid]) - (sum[r - 1] - sum[mid]);     // mid-1 -> R
        cout << ans << endl;
    }
    return 0;
}

树状数组

#include 
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, q, l, r, a;
ll c[2][N]; // 0-人数 1-坐标和
int lowbit(int x) { return x & -x; }
void add(int k, int x, int val)
{
    while (x <= n)
    {
        c[k][x] += val;
        x += lowbit(x);
    }
}
ll query(int k, int x)
{
    ll res = 0;
    while (x >= 1)
    {
        res += c[k][x];
        x -= lowbit(x);
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> a;
        add(0, a, 1);
        add(1, a, a);
    }
    while (q--)
    {
        cin >> l >> r;
        if (l > r)
            swap(l, r);
        int mid = l + r >> 1;
        ll ans = 0;
        ll cntl = query(0, l);
        ll suml = query(1, l);
        ans += cntl * l - suml;
        ll cnt_mid = query(0, mid);
        ll sum_mid = query(1, mid);
        ans += (sum_mid - suml) - (cnt_mid - cntl) * l;
        ll cntr = query(0, r);
        ll sumr = query(1, r);
        ans += (cntr - cnt_mid) * r - (sumr - sum_mid);
        ll cntall = query(0, 100000);
        ll sumall = query(1, 100000);
        ans += (sumall - sumr) - r * (cntall - cntr);
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(树状数组,牛客练习)