刷题总结——棘手的操作(bzoj2333)

题目:

题目背景

SCOI2011 DAY2 T1

题目描述

有 N 个节点,标号从 1 到 N ,这 N 个节点一开始相互不连通。第i个节点的初始权值为 a[i] ,接下来有如下一些操作:
U x y:     加一条边,连接第 x 个节点和第 y 个节点
A1 x v:    将第 x 个节点的权值增加 v
A2 x v:    将第 x 个节点所在的连通块的所有节点的权值都增加 v
A3 v:      将所有节点的权值都增加 v
F1 x:      输出第 x 个节点当前的权值
F2 x:      输出第 x 个节点所在的连通块中,权值最大的节点的权值
F3:        输出所有节点中,权值最大的节点的权值

输入格式

输入的第一行是一个整数 N ,代表节点个数。
接下来一行输入 N 个整数,a[1], a[2], …, a[N],代表 N 个节点的初始权值。
再下一行输入一个整数 Q ,代表接下来的操作数。
最后输入 Q 行,每行的格式如题目描述所示。

输出格式

对于操作 F1, F2, F3,输出对应的结果,每个结果占一行。

样例数据 1

输入  [复制]


0 0 0 

A1 3 -20 
A1 2 20 
U 1 3 
A2 1 10 
F1 3 
F2 3 
A3 -10 
F3

输出

-10 
10 
10

备注

【数据范围】
对于 30% 的数据,保证 N<=100,Q<=10000
对于 80% 的数据,保证 N<=100000,Q<=100000
对于 100% 的数据,保证 N<=300000,Q<=300000
对于所有的数据,保证输入合法,并且 -1000<=v, a[1], a[2], …, a[N]<=1000

题解:

这道题可以用可并堆或者线段树来写····

然而如果用可并堆得话得会删除特定的点的操作··然而··我一直认为如果有删除特定的点的操作还不如用splay···

然后我就打了2个小时的splay····最后写不出来弃疗了·····这道题用可并堆做出来的人是真正的勇士·····

也从这道题中发现了自己代码能力的不足··一是粗心大意···二是不懂得简化代码··搞得写到最后脑袋发蒙·····以后多看看那些写得简洁的人的代码····

然后我就用线段树来写了··下面将线段树的方法:

其实用线段树写的话最重要的是要维护一个dfs序···在我们进行离线操作后dfs出来的dfs序必须保证:任意时间所有在同一个并查集里的点的dfs序是连续的···

怎么做到呢?有两点:

第一点:我们离线时侯的建边的顺序必须是和我们在离线后dfs时的顺序是一致的

打个比方···题目中先是连1,2的边··后连1,3的边··那么我们在dfs时候必须先走1,2的边····

要做到这一点很简单··考虑到临接表加边的顺序与dfs时的顺序是反的···因此我们每次不直接加边··先将边保存起来···最后将所有边倒着加入即可······

第二点:dfs顺序必须和加边的顺序一致

依然举上面的例子···如果我们连了2,1的边···那么我们必须保证先dfs2这个点,但如果按正常顺序dfs,我们会先枚举到1这个点,如果1在连了21之后与其他点连了边··直接dfs就会发生错误····

所以我们可以将1打个标记···在枚举到1的时候直接略过它··这样就可以保证会先dfs到2,再dfs到1···与1相连的其他点也会被dfs到····换句话说··如果连了一条边AB,我们要给B打一个标记····保证dfs时先dfs到点A,再dfs到点B

解决了dfs序的顺序后接下来就是线段树的基础操作了

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=3e5+5;
const int inf=0x3f3f3f3f;
struct node
{
  int x,y,op;
}q[N];
struct node1
{
  int from,to;
}e[N];
int dfn[N],last[N],tree[N*5],father[N],tag[N*5],n,m,num[N],val[N],fst[N],go[N],nxt[N],tot,to,cnt,tol,pre[N];
bool jud[N];
inline void comb(int a,int b)
{
  nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b;
}
inline int getfa(int u)
{
  if(father[u]==u)  return u;
  else return father[u]=getfa(father[u]);
}
inline int R()
{
  char c;int f=0,i=1;
  for(c=getchar();(c<‘0‘||c>‘9‘)&&c!=‘-‘;c=getchar());
  if(c==‘-‘)  c=getchar(),i=-1;
  for(;c<=‘9‘&&c>=‘0‘;c=getchar())
    f=(f<<3)+(f<<1)+c-‘0‘;
  return f*i;
}
inline void dfs(int u)
{
  last[u]=dfn[u]=++cnt;
  for(int e=fst[u];e;e=nxt[e])  dfs(go[e]);
}
inline void add(int k,int v)
{
  tree[k]+=v;tag[k]+=v;
}
inline void pushdown(int k)
{
  if(tag[k])
  {
    add(k*2,tag[k]);
    add(k*2+1,tag[k]);
    tag[k]=0;
  }
}
inline void build(int k,int l,int r)
{
  if(l==r)
  {
    tree[k]=val[l];return;
  }
  int mid=(l+r)/2;
  build(k*2,l,mid);build(k*2+1,mid+1,r);
  tree[k]=max(tree[k*2],tree[k*2+1]);
  return;
}
inline void modify(int k,int l,int r,int x,int y,int v)
{
  if(l>=x&&r<=y)
  {
    add(k,v);return;
  }
  int mid=(l+r)/2;
  pushdown(k);
  if(x<=mid)  modify(k*2,l,mid,x,y,v);
  if(y>mid)  modify(k*2+1,mid+1,r,x,y,v);
  tree[k]=max(tree[k*2],tree[k*2+1]);
}
inline int query(int k,int l,int r,int x,int y)
{
  if(l>=x&&r<=y)
    return tree[k];
  int mid=(l+r)/2;int temp=-inf;
  pushdown(k);
  if(x<=mid)  temp=max(temp,query(k*2,l,mid,x,y));
  if(y>mid)  temp=max(temp,query(k*2+1,mid+1,r,x,y));
  return temp;
}
int main()
{
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
  memset(tree,-inf,sizeof(tree));
  n=R();for(int i=1;i<=n;i++)  num[i]=R(),father[i]=i;
  m=R();char s[5];memset(jud,true,sizeof(jud));
  for(int i=1;i<=m;i++)
  {
    scanf("%s",s);
    if(s[0]==‘U‘)
    {
      q[i].op=1;q[i].x=R(),q[i].y=R();
      int a=getfa(q[i].x),b=getfa(q[i].y);
      if(a!=b)  father[a]=b,jud[a]=false,e[++tol].from=b,e[tol].to=a;
    }
    else if(s[0]==‘A‘)
    {
      if(s[1]==‘1‘)  q[i].op=2,q[i].x=R(),q[i].y=R();
      if(s[1]==‘2‘)  q[i].op=3,q[i].x=R(),q[i].y=R();
      if(s[1]==‘3‘)  q[i].op=4,q[i].x=R();
    }
    else if(s[0]==‘F‘)
    {
      if(s[1]==‘1‘)  q[i].op=5,q[i].x=R();
      if(s[1]==‘2‘)  q[i].op=6,q[i].x=R();
      if(s[1]==‘3‘)  q[i].op=7;
    }
  }
  for(int i=tol;i;i--)  comb(e[i].from,e[i].to);
  for(int i=1;i<=n;i++)
  {
      father[i]=i;
      if(!dfn[i]&&jud[i])
        dfs(i);
  }
  for(int i=1;i<=n;i++)  pre[i]=dfn[i],val[dfn[i]]=num[i];build(1,1,n);
  for(int i=1;i<=m;i++)
  {
    if(q[i].op==1)
    {
      int a=getfa(q[i].x),b=getfa(q[i].y);
      if(a!=b)  father[a]=b,dfn[b]=max(dfn[b],dfn[a]),last[b]=min(last[b],last[a]);
    }
    if(q[i].op==2)
    {
      int a=q[i].x,b=q[i].y;
      modify(1,1,n,pre[a],pre[a],b);
    }
    if(q[i].op==3)
    {
      int a=getfa(q[i].x),b=q[i].y;
      modify(1,1,n,last[a],dfn[a],b);
    }
    if(q[i].op==4)
    {
      int a=q[i].x;
      modify(1,1,n,1,n,a);
    }
    if(q[i].op==5)
    {
      int a=q[i].x;
      printf("%d\n",query(1,1,n,pre[a],pre[a]));
    }
    if(q[i].op==6)
    {
      int a=getfa(q[i].x);
      printf("%d\n",query(1,1,n,last[a],dfn[a]));
    }
    if(q[i].op==7)
      printf("%d\n",query(1,1,n,1,n));
  }
  return 0;
}
时间: 2024-09-11 19:44:04

刷题总结——棘手的操作(bzoj2333)的相关文章

【BZOJ2333】棘手的操作(左偏树,STL)

[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 (确实很显然,只是写死人...) 然后对于全局的最大值而言 搞一个\(multi\)来水 看起来真的简单.. 写起来真的想死... 记住:要特判一下已经联通的块就不要再去\(Merge\)了 #include<iostream> #include<cstdio> #include<

bzoj2333[SCOI2011]棘手的操作

bzoj2333[SCOI2011]棘手的操作 题意: 有N个节点,M个操作:连接两个节点.单个节点的权值增加v.节点所在的连通块的所有节点的权值增加v.所有节点的权值增加v.询问节点当前的权值.询问节点所在的连通块中权值最大的节点的权值.询问所有节点中权值最大的节点的权值.N,M≤300000 题解: 可并堆,虽然听说配对堆非常快,但教程太少了不会写,所以去学了斜堆,比较好写.斜堆实际上是一棵二叉树,核心是合并操作,这是一个递归过程,有点像treap的删除操作.斜堆保证复杂度的方法是每次递归合

【bzoj2333】 SCOI2011—棘手的操作

http://www.lydsy.com/JudgeOnline/problem.php?id=2333 (题目链接) 题意 N个节点维护一些操作.. Solution 我们用可并大根堆进行维护. 对于每个连通块建一个局部可并堆,因为要询问全局最大值,所以还要对全局建一个全局可并堆记录之前局部可并堆堆顶元素. U:合并x所在的堆以及y所在的堆,并在全局堆中删除合并前的局部堆堆顶元素,因为它合并以后已经不是其连通块的堆顶了. A1:在堆中删除,更新后再加入堆 A2:找到其堆顶,对堆顶进行修改并打上

用js刷题的一些坑

leecode可以用js刷题了,我大js越来越被认可了是吧.但是刷题中会因为忽略js的一些特性掉入坑里.我这里总结一下我掉过的坑. 坑1:js中数组对象是引用对象 js中除了object还有数组对象也是引用对象,这点常常被忽视,所以在递归的时候传递数组要用arr.slice(0)这样复制一个一样的新数组,不然会出现你传入的数组会被同级的递归改变,结果就不对了. 所以只要数组复制的地方最好都要这么写,除非你真的想引用.而且注意是slice不是splice这两个方法差别很大,你如果用splice(0

LeetCode刷题之一:寻找只出现一次的数字

投简历的时候看到了个刷题网站,http://www.nowcoder.com/527604,就做了一套题,现记录下来. 题目为: Given an array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it withou

刷题记录

刷题啦,刷题啦,咱也刷算法题. 先从牛客网的JS方面刷起,接着刷数据结构和算法,然后去刷leetcode,这儿记载自己从出错的地方. 1.题目描述 移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回 . 没有认真思考,写下了如下的答案 function removeWithoutCopy(arr, item) { for(i = 0; i < arr.length; i++) { if( arr[i] === item) { arr.spli

BZOJ 刷题记录 PART 4

[BZOJ1143]CTSC的题目...先用floyed传递闭包,然后直接上匈牙利算法. [BZOJ1452]从未写过的二维树状数组.好像很简单.. struct two_bit { int f[305][305]; inline void add(int x,int z,int A) { for (;x<=n;x+=L(x)) for (int y=z;y<=m;y+=L(y)) f[x][y]+=A; } inline int ask(int x,int z) { int ans=0; f

leetcode 刷题之路 93 Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 将k个有序链表合并成一个有序链表. 思路,之前做过两个有序链表的合并操作,对于k个链表,我们最先想到的就是能不能转化为我们熟悉的两个链表的合并,可以我们先将k个链表分为两部分,先对这两部分完成链表的有序合并操作,再对合并得到的两个链表进行合并,这是一个递归性质的描述,采用递归很容易实现这个程序,和数组

【leetcode刷题笔记】Maximal Rectangle

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area. 题解,很巧妙的一道题,对于一个0-1矩阵,它的每一行及以上都可以看作一个直方图(如下图所示),利用Largest Rectangle in Histogram的方法,可以在O(n)的时间搜索出这一行及以上的直方图中面积最大的矩形,对矩阵的每一行依次做这个操作,就可