首页 > 编程语言 >P7619 [COCI2011-2012#2] RASPORED

P7619 [COCI2011-2012#2] RASPORED

时间:2022-10-06 12:12:27浏览次数:53  
标签:return RASPORED int COCI2011 merge P7619 key now root

此题解使用平衡树解决。

1、原始情况

首先,我们考虑未修改的情况。设 \(L_i\) 为吃饭时间,\(a_i\) 为制作所需时间。对于 \(n\) 个居民,假设我们不对做披萨的顺序进行修改,即按照原始顺序的话,可以写出答案:

\(ans = (L_1 - a_1) + [L_2 - (a_1 + a_2] + [L_3 - (a_1 + a_2 + a_3)] ......\)

以此类推,整理后即可得到:

\(ans = (L_1 + L_2 + L_3 + ..... + L_n) - [na_1 + (n-1)a_2 + (n-2)a_3 + ...... + a_n]\)

不难看出,送披萨的先后顺序对于 \(L_i\) 来说是没有影响的。而对于后半部分,我们要想使得总答案最大,肯定要使得 \(a_1\) 最小, \(a_2\) 次大,以此类推。因此,只需要通过一次排序,我们便能轻松求出修改前最大的小费收益。

2、修改

接下来考虑每次的修改。首先,对于 \(L_i\) 的变化,我们直接加减即可。重点考虑 \(a_i\) 的变化。不妨如此假设:令 \(a_i\) 原先的排名为 \(l\), 而其修改后的排名为 \(r (l \le r)\), 可以看到,只有区间 \((l,r)\) 内部的数受到了影响,他们的系数全部减小 \(1\), 即对于该次修改,我们要减掉 \(\sum a_i (i \in (l,r))\) 。当 \(l \ge r\) 的时候同理,反过来即可。

3、具体操作

考虑到我们需要提取一定区间内部的所有数,使用 \(FHQ-Treap\) 可以很好的解决这个问题。在传统写法的基础上,记 \(w\) 为每个节点的子树大小之和,方便快速查询。 (想法来自线段树)

细节处理请见代码注释。

4、后记

  • 不开 long long 见祖宗!
  • 每次修改后的数据应该保留,手摸样例可以发现。

上代码(常数略大,大家可自行优化一下。)

#include <bits/stdc++.h>
using namespace std;
#define N 2000010
#define ll long long
#define int long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

int n, Q;
struct people{
  int L, T; 
  int id; 
} a[N]; 

bool cmp1(people a, people b){
  return a.T < b.T; 
}

bool cmp2(people a, people b){
  return a.id < b.id; 
}

ll ans = 0; 

/*经典 FHQ-Treap 写法,不过多解释。*/
int root = 0; 

struct node{
  int siz, val, ch[2]; 
  int pri, w;     // w 为总和, val 为当前点的值
} t[N]; 

#define lson ch[0]
#define rson ch[1]

int tot = 0;
inline int build(int x){
  t[++tot].siz = 1;
  t[tot].val = x; 
  t[tot].pri = rand(); 
  t[tot].lson = t[tot].rson = 0;
  t[tot].w = x; 
  return tot; 
}

inline void pushup(int now){
  t[0].siz = t[0].w = t[0].val = 0; 
  t[now].siz = t[t[now].lson].siz + t[t[now].rson].siz + 1; 
  t[now].w = t[t[now].lson].w + t[t[now].rson].w + t[now].val; 
  return ; 
}

void split(int now, int key, int& x, int& y){  // 分裂
  if(!now){
    x = y = 0;
    return ; 
  }
  if(t[now].val <= key){
    x = now; 
    split(t[now].rson, key, t[now].rson, y); 
  }
  else {
    y = now; 
    split(t[now].lson, key, x, t[now].lson); 
  }
  pushup(now); 
  return ; 
}

int merge(int x, int y){
  if(!x || !y) return x + y;
  if(t[x].pri > t[y].pri){
    t[x].rson = merge(t[x].rson, y); 
    pushup(x);
    return x; 
  }
  else{
    t[y].lson = merge(x, t[y].lson); 
    pushup(y);
    return y; 
  }
  return 0; 
}

void insert(int key){
  int x, y; 
  split(root, key - 1, x, y); 
  root = merge(merge(x, build(key)), y); 
  return ; 
}

void del(int key){
  int x, y, z; 
  split(root, key, x, z); 
  split(x, key - 1, x, y); 
  if(y) y = merge(t[y].lson, t[y].rson); 
  root = merge(merge(x, y), z); 
  return ; 
}

int get_ans(int ori, int key){  // 处理每次修改后 T 的变化
  if(ori == key) return 0; 
  int x, y, z, sum = 0; 
  split(root, ori, x, y);         /*对于大小相同的重复节点的处理:直接把当前点认作排名最后的一个点。因为他们的先后顺序不影响答案.*/
  sum += (n - t[x].siz + 1) * ori;   
  root = merge(x, y); 
  del(ori); 
  // 消除原有影响

  insert(key); 
  if(key > ori){   // 注意分类讨论,提取区间内的所有数,建议画图讨论
    split(root, ori, x, z); 
    split(z, key, y, z); 
    sum -= t[y].w - key; 
    root = merge(merge(x, y), z); 
  }
  else {
    split(root, ori, x, z); 
    split(x, key, x, y); 
    sum += t[y].w; 
    root = merge(merge(x, y), z); 
  }
  split(root, key, x, y);   // 不要忘了这个数自己
  sum -= (n - t[x].siz + 1) * key; 
  root = merge(x, y); 
  return sum; 
}

signed main(){
  read(n); read(Q);
  for(int i = 1; i <= n; i++){
    read(a[i].L), read(a[i].T); 
    a[i].id = i; 
  }
  sort(a + 1, a + n + 1, cmp1); 
  for(int i = 1; i <= n; i++){
    ans += a[i].L; 
    ans -= (n - i + 1) * a[i].T; 
  }
  cout << ans << endl; 
  for(int i = 1; i <= n; i++)
    insert(a[i].T); 
  sort(a + 1, a + n + 1, cmp2); 
  while(Q--){
    int opt, L, T; 
    read(opt), read(L), read(T); 
    ans -= a[opt].L; 
    ans += L; 
    a[opt].L = L; 
    ans += get_ans(a[opt].T, T); 
    a[opt].T = T; 
    printf("%lld\n", ans); 
  }
  return 0;
}

标签:return,RASPORED,int,COCI2011,merge,P7619,key,now,root
From: https://www.cnblogs.com/wondering-world/p/16757324.html

相关文章

  • luoguP8085 [COCI2011-2012#4] KRIPTOGRAM 题解(KMP)
    /*给定明文和密文,密文与明文的某个字串格式相同,找出密文出现的最早位置。如:明文aaabcdabc 密文xy ans:3解:容易想到KMP算法。可以发现,密文和对应子串的格式相同......