9.3洛谷数据结构2

P 4513  单点修改

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define ll long long
#define INF 0x3fffff
#define clr(x) memset(x,0,sizeof(x))

using namespace std;

inline int read()
{
    register int ret=0,c=getchar(),b=1;
    while(!isdigit(c))b=c=='-'?-1:1,c=getchar();
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret*b;
}

#define M 100005

struct tree2
{
    tree2 *lson,*rson;
    ll x,lazy;
}dizhi[M<<1],*root=&dizhi[0];

int n,m,t=1,a[M];

void bulid(tree2 *tree,int l,int r)
{
    if(l==r)
    {
        tree->x=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    tree->lson=&dizhi[t++];
    tree->rson=&dizhi[t++];
    bulid(tree->lson,l,mid);
    bulid(tree->rson,mid+1,r);
    tree->x=tree->lson->x+tree->rson->x;
}

void pushdown(tree2 *tree,int l,int r)
{
    if(!tree->lazy)return ;
    int mid=(l+r)>>1;
    tree->lson->x+=tree->lazy*(mid-l+1);
    tree->rson->x+=tree->lazy*(r-mid);
    tree->lson->lazy+=tree->lazy;
    tree->rson->lazy+=tree->lazy;
    tree->lazy=0;
}

void change(tree2 *tree,int l,int r,int x,int y,int d)
{
    if(x<=l&&y>=r)
    {
        tree->x+=(ll)d*(r-l+1);
        tree->lazy+=d;
        return ;
    }
    pushdown(tree,l,r);
    int mid=(l+r)>>1;
    if(x<=mid)change(tree->lson,l,mid,x,y,d);
    if(y>mid) change(tree->rson,mid+1,r,x,y,d);
    tree->x=tree->lson->x+tree->rson->x;
}

ll query(tree2 *tree,int l,int r,int x,int y)
{
    if(x<=l&&y>=r)
        return tree->x;
    pushdown(tree,l,r);
    int mid=(l+r)>>1;
    ll t1=0,t2=0;
    if(x<=mid)t1=query(tree->lson,l,mid,x,y);
    if(y>mid)t2=query(tree->rson,mid+1,r,x,y);
    return t1+t2;
}

int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    bulid(root,1,n);
    for(int i=1;i<=m;i++)
    {
        int mode=read();
        if(mode==1)
        {
            int a=read(),b=read(),c=read();
            change(root,1,n,a,b,c);
        }
        else
        {
            int a=read(),b=read();
            printf("%lld\n",query(root,1,n,a,b));
        }
    }

 

ST表

算法用途

Sparse Table,又称ST表,稀疏表。运用倍增的思想,可以解决RMQ,LCA等问题。其优点是在线查询。预处理复杂度为O(nlogn),查询复杂度为O(1)。

算法思想

运用倍增的思想,num[i][j]表示区间[i,i+(1<<

j)]的值。然后进行预处理求出num数组。

算法实现

以求最大值为例

① 对于预处理,有如下转移方程式:

num[i][j]=max(num[i][j-1],num[i+(1<<j-1)][j-1]);
  • 1

这是什么东西啊??

我们来推一遍:
其实只是把[i,i+(1<<

j)]这个区间给分成两块,一块是[i,i+(1<<j-1)],另一块是[i+(1<<j-1),i+(1<<j)](i+(1<<j)==i+(1<<j-1)+(1<<

j-1))。

所以这个区间的最大值就是这两个区间的最大值的较大者。

然后就推好啦!

② 对于查询[x,y]之间的最大值,我们可以这样:

int j=log2(y-x+1);
printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j]));

这又是什么东西??

我们再来推一遍:
num[x][j]表示[x,x+(1<<

j)]这段区间,本来是刚好的,但是因为j是向下取整的,因此不一定取得完。取到的区间长度实际为1<<j-1。此时我们可以从y-(1<<j-1)=y-(1<<j)+1这个点开始取(取重了也没事),取相同的长度。然后在这两者之间取较大者即可。

P3865

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 150000
using namespace std;
int n,m;
int num[MAXN+5][18];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;
    return *l++;
}
int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9'){ num=num*10+ch-48; ch=readc(); }
    return num;
}
void make(){
    for (int i=1;i<18;i++)
        for (int j=1;j+(1<<i)-1<=n;j++)
            num[j][i]=max(num[j][i-1],num[j+(1<<i-1)][i-1]);
}
int main(){
    n=_read(); m=_read();
    for (int i=1;i<=n;i++)
        num[i][0]=_read();
    make();
    for (int i=1;i<=m;i++){
        int x=_read(),y=_read();
        int j=log2(y-x+1);
        printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j]));
    }
    return 0;
}

线段树  扫描线

hdu1542 

/*
1.保存矩形的上下边界,并且重要的,记录他们是属于上还是下,然后按高度升序排序
2.保存竖线坐标,并且去重,是为了离散化
3.以保存的上下边界数组去更新
*/
#include <cstdio>
#include <cstring>
#include<iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAX 110
#define LCH(i) ((i)<<1)
#define RCH(i) ((i)<<1 | 1)

struct segment //保存矩形上下边界
{
  double l,r,h; //左右横坐标,纵坐标
  int f; //-1为下边界,1为上边界
}ss[2*MAX];
struct node //线段树节点
{
  int l,r;
  int cnt; //该节点被覆盖的情况
  double len; //该区间被覆盖的总长度
  int mid()
  { return (l+r)>>1; }
}tt[2*MAX*4];
double pos[2*MAX];
int nums;

int cmp(struct segment a ,struct segment b)
{
  return a.h<b.h;
}

void build(int a, int b ,int rt)
{
 tt[rt].l=a; tt[rt].r=b; tt[rt].cnt=0; tt[rt].len=0;
 if(a==b) return ;
 int mid=tt[rt].mid();
 build(a,mid,LCH(rt));
 build(mid+1,b,RCH(rt));
}

int binary(double key ,int low, int high)
{
   while(low<=high)
   {
      int mid=(low+high)>>1;
      if(pos[mid] == key) return mid;
      else if(key < pos[mid]) high=mid-1;
      else                    low=mid+1;
   }
   return -1;
}

void get_len(int rt)
{
   if(tt[rt].cnt) //非0,已经被整段覆盖
      tt[rt].len = pos[tt[rt].r+1] - pos[tt[rt].l];
   else if(tt[rt].l == tt[rt].r) //已经不是一条线段
      tt[rt].len = 0;
   else //是一条线段但是又没有整段覆盖,那么只能从左右孩子的信息中获取
      tt[rt].len = tt[LCH(rt)].len + tt[RCH(rt)].len ;
}

void updata(int a, int b ,int val ,int rt)
{
   if(tt[rt].l==a && tt[rt].r==b) //目标区间
   {
      tt[rt].cnt += val; //更新这个区间被覆盖的情况
      get_len(rt);  //更新这个区间被覆盖的总长度
      return ;
   }
   int mid=tt[rt].mid();
   if(b<=mid) //只访问左孩子
      updata(a,b,val,LCH(rt));
   else if(a>mid) //只访问有孩子
      updata(a,b,val,RCH(rt));
   else //左右都要访问
   {
      updata(a,mid,val,LCH(rt));
      updata(mid+1,b,val,RCH(rt));
   }
   get_len(rt); //计算该区间被覆盖的总长度
}

int main()
{
  int Case=0;
  int n;
  while(scanf("%d",&n)!=EOF && n)
  {
    nums=0;
    for(int i=0; i<n; i++)
    {
      double x1,y1,x2,y2;
      scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
      ss[nums].l=x1;  ss[nums].r=x2; ss[nums].h=y1; ss[nums].f=1;
      //记录上边界的信息
      ss[nums+1].l=x1; ss[nums+1].r=x2; ss[nums+1].h=y2; ss[nums+1].f=-1;
      //记录下边界的信息
      pos[nums]=x1; pos[nums+1]=x2;
      //记录横坐标
      nums += 2;

    }

    sort(ss,ss+nums,cmp); //横线按纵坐标升序排序
    sort(pos,pos+nums); //横坐标升序排序
    //for(int i=0; i<nums; i++) printf("%.2lf %.2lf  %.2lf\n",ss[i].l,ss[i].r,ss[i].h);
    int m=1;
    for(int i=1; i<nums; i++)
      if(pos[i]!=pos[i-1]) //去重
        pos[m++]=pos[i];

    build(0,m-1,1);  //离散化后的区间就是[0,m-1],以此建树
    double ans=0;
    for(int i=0; i<nums; i++) //拿出每条横线并且更新
    {
       int l=binary(ss[i].l,0,m-1);
       int r=binary(ss[i].r,0,m-1)-1;
       updata(l,r,ss[i].f,1); //用这条线段去更新
       ans += (ss[i+1].h-ss[i].h)*tt[1].len;
       //printf("%.2lf\n",ans);
    }
    printf("Test case #%d\n",++Case);
    printf("Total explored area: %.2f\n\n",ans);
  }
  return 0;
}

P1904(离散化)

猜你喜欢

转载自www.cnblogs.com/zyddd915/p/11455861.html