Loading

【做题笔记】DS 专题训练

P1456 Monkey King

不会左偏树怎么办,直接启发式合并。

开个并查集,来判断敌友关系。随后遇上需要合并 \(x, y\),直接找到祖先然后启发式合并即可。

合并并查集同时合并一下堆,答案跟着一起维护即可,可以证明均摊时间复杂度为 \(O(q\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)

using namespace std;

const int N = 1e5 + 10;

int n, m, a[N], f[N];

priority_queue<int> q[N];

int find(int x) {
  return (x == f[x] ? x : f[x] = find(f[x]));
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  while(cin >> n) {
    For(i,1,n) priority_queue<int>().swap(q[i]);
    For(i,1,n) {
      cin >> a[i];
      q[i].push(a[i]);
      f[i] = i;
    }
    cin >> m;
    For(i,1,m) {
      int x, y;
      cin >> x >> y;
      if(find(x) == find(y)) cout << "-1\n";
      else {
        x = find(x), y = find(y);
        int X = q[x].top(), Y = q[y].top();
        q[x].pop(), q[y].pop();
        q[x].push(X/2), q[y].push(Y/2);
        if(q[x].size() > q[y].size()) swap(x, y);
        while(!q[x].empty()) {
          q[y].push(q[x].top());
          q[x].pop();
        }
        cout << q[y].top() << '\n';
        f[find(x)] = find(y);
      }
    }
  }
  return 0;
}

P3567 [POI2014] KUR-Couriers

考虑暴力将每一次询问 \([l,r]\) 中的数插入一个桶中,假设给定了桶的一个值域 \([L,R]\),设桶值域中的总和为 \(val\),我们发现若 \(2 \times val > r-l+1\) 则一定存在一个数的个数超过总区间长度的一半。

于是将所有数插入主席树中,对于询问 \([l,r]\),我们只要查找递归主席树 \([l,r]\) 内的左右儿子值域 \([L,mid],[mid+1,R]\) 来判断即可。

时间复杂度 \(O(q\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)

using namespace std;

const int N = 5e5 + 10;

struct Node {
  int ls, rs, val;
} t[N * 50];

int n, m, a[N], root[N], idx;

void pushup(int p) {
  t[p].val = t[t[p].ls].val + t[t[p].rs].val;
}

void upd(int last, int &p, int l, int r, int x, int k) {
  p = ++idx;
  t[p] = t[last];
  if(l == r) {
    t[p].val += k;
    return ;
  }
  int mid = l + r >> 1;
  if(x <= mid) upd(t[last].ls, t[p].ls, l, mid, x, k);
  else upd(t[last].rs, t[p].rs, mid + 1, r, x, k);
  pushup(p);
}

int query(int p1, int p2, int x, int l, int r) {
  if(l == r) return l;
  int mid = l + r >> 1;
  if(2 * (t[t[p2].ls].val - t[t[p1].ls].val) > x) return query(t[p1].ls, t[p2].ls, x, l, mid);
  if(2 * (t[t[p2].rs].val - t[t[p1].rs].val) > x) return query(t[p1].rs, t[p2].rs, x, mid + 1, r);
  return 0;
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n >> m;
  For(i,1,n) {
    cin >> a[i];
    upd(root[i-1], root[i], 1, n, a[i], 1);
  }
  For(i,1,m) {
    int l, r;
    cin >> l >> r;
    cout << query(root[l-1], root[r], r-l+1, 1, n) << '\n';
  }
  return 0;
}

P5482 [JLOI2011] 不等式组

好题。

首先看到维护不等式很容易想到树状数组。然后考虑一次函数的代价,也就是 \(a\) 的正负会带来什么影响。当 \(a>0\) 时,\(x>\dfrac{c-b}{a}\);当 \(a<0\) 时,\(x<\dfrac{c-b}{a}\);因为 \(x\) 只能为整数,所以上述条件与当 \(a>0\) 时, \(x>\left\lfloor\dfrac{c-b}{a}\right\rfloor\);当 \(a<0\) 时,\(x<\left\lceil\dfrac{c-b}{a}\right\rceil\) 等价。

于是就用值域分别为 \([-10^6,0)\)\([0,10^6]\) 的两个树状数组来维护即可。注意要判断一下恒成立和恒不成立的不等式,并且要注意删除的时候记得消除贡献并标记删除。

时间复杂度 \(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)
#define MAX_F 2000002
#define L 1000001

using namespace std;

const int N = 2e6 + 10;

struct Node {
  int a, b, c, id;
} p[N];

int n, cnt, lon, t1[N], t2[N], k[N];

bool del[N];

int lb(int x) {
  return x & -x;
}

int qry(int x, int op) {
  x += L;
  int ans = 0;
  for (int i = x; i; i -= lb(i)) {
    ans += (op == 1 ? t1[i] : t2[i]);
  }
  return ans;
}

void upd(int x, int k, int op) {
  x += L;
  for (int i = x; i <= MAX_F; i += lb(i)) {
    (op == 1 ? t1[i] : t2[i]) += k;
  }
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  while(n--) {
    string op;
    int a, b, c, i, x;
    cin >> op;
    
    if(op == "Add") {
      cin >> a >> b >> c;
      p[++cnt] = (Node){a, b, c, (a>0)+1};
      if(!a) {
        if(b > c) p[cnt].id = 3, lon++;
        else p[cnt].id = 0; 
      } else if(a < 0) {
        k[cnt] = ceil((c * 1.0 - b) / a);
        p[cnt].id = 1;
        if(k[cnt] < -1e6) p[cnt].id = 0;
        else if(k[cnt] > 1e6) {
          lon++;
          p[cnt].id = 3;
        } else {
          upd(k[cnt], 1, 1);
        }
      } else {
        k[cnt] = floor((c * 1.0 - b) / a);
        p[cnt].id = 2;
        if(k[cnt] > 1e6) p[cnt].id = 0;
        else if(k[cnt] < -1e6) {
          lon++;
          p[cnt].id = 3;
        } else {
          upd(k[cnt], 1, 2);
        }
      }
    } else if(op == "Del") {
      cin >> i;
      if(del[i]) continue;
      del[i] = 1;
      if(p[i].id == 3) lon--;
      else if(p[i].id == 1) upd(k[i], -1, 1);
      else if(p[i].id == 2) upd(k[i], -1, 2);
    } else {
      cin >> x;
      cout << qry(x-1, 2) + (qry(1e6, 1) - qry(x, 1)) + lon << '\n';
    }
  }
  return 0;
}
posted @ 2024-08-20 09:30  Daniel_yzy  阅读(9)  评论(0编辑  收藏  举报