首页 > 其他分享 >2022.12.18 ~ 2022.12.24 一周学习记录

2022.12.18 ~ 2022.12.24 一周学习记录

时间:2022-12-25 15:44:47浏览次数:73  
标签:24 right int 18 tr fa root 2022.12 left

2022.12.18 模拟退火学习
模拟退火是一种常用的随机化算法 ,当答案是一个连续的函数时,我们就可以考虑用模拟退火进行求解。
注意调参数(看rp)

伪代码:

void SA()//模拟退火
{
    for(double T=10000;T>1e-5;T=T*0.975)
    {
        int p=更改前的答案;
        随机更改;
        int q=更改后的答案;
        int change=q-p;
        if(exp(change/T)<(double)rand()/RAND_MAX)
        	以一定概率接受更改后的结果
        else 恢复
    }
}

练习题目
[AHOI2014/JSOI2014]保龄球
将次序转化成序列,每次随机交换两个位置,判断是否合法后开始不断更新最优解即可。
[HAOI2006]均分数据
考虑贪心,将每个数加到当前和最小的组里,每次随机交换两个元素,不断更新最优解即可。


2022.12.19 ~ 2022.12.21 文艺平衡树(\(splay\))学习
\(splay\) 是一种平衡树,维护了中序遍历的不变性,核心操作有两个:rotatesplay

\(rotate\) :通过旋转不断将节点移动,但移动的同时也要保持中序遍历的不变性,可以手画一下推结论,代码如下:

struct node{
    int left,right,siz,fa,v;
    void init(int father,int vv)
    {
        fa=father,v=vv;
        siz=1;
    }
} tr[N];//平衡树
void pushup(int k)
{
    tr[k].siz=tr[tr[k].left].siz+tr[tr[k].right].siz+1;
}
void rotate(int x)
{
    int y=tr[x].fa,z=tr[y].fa;
    if(tr[z].right==y) tr[z].right=x;
    else tr[z].left=x;
    tr[x].fa=z;
    if(tr[y].left==x) tr[y].left=tr[x].right,tr[tr[x].right].fa=y,tr[x].right=y,tr[y].fa=x;
    else tr[y].right=tr[x].left,tr[tr[x].left].fa=y,tr[x].left=y,tr[y].fa=x;
    pushup(y),pushup(x);
}

splay:将一个节点转到另一个节点的下面(其中 \(k=0\) 表示转到根节点)需要分三个点是否在一条直线上讨论,在一条直线上要 \(rotate(y) \;rotate(x)\) ,否则需要 \(rotate(x)\) 两次,代码如下:

void splay(int &root,int x,int k)
{
    while(tr[x].fa!=k)
    {
        int y=tr[x].fa,z=tr[y].fa;
        if(z!=k)
        {
            if((tr[z].right==y) ^ (tr[y].right==x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    if(k==0) root=x;
}

insert:插入操作,插入后需要记得将其转到根节点

void insert(int x)
{
    int u=root,p=0;
    while(u!=0)
    {
        p=u;
        if(x>tr[u].v) u=tr[u].right;
        else u=tr[u].left;
    }
    u=++idx;
    if(p!=0)
    {
        if(x>tr[p].v) tr[p].right=u;
        else tr[p].left=u;
    }
    tr[u].init(p,x);
    splay(u,0);
}

练习题目
【模板】文艺平衡树
区间翻转操作,维护一个是否翻转的懒标记即可
[NOI2004] 郁闷的出纳员
需要记录一个全局的懒标记,并需要区间删除和查询排名为\(k\)的数
区间删除:找到这个区间的前一个数\(L\)和后一个数\(R\),将\(L\)转到根节点,再将\(R\)转到\(L\)的下面,考虑其中序遍历的不变性,这样\(R\)的左子树就是要删除的那段区间,直接删除即可
查询排名为\(k\)的数:从根节点开始遍历,将排名与左子树的大小进行比较。
如果排名更大,先特判是不是当前节点,也就是判断左子树的大小加\(1\)是否等于\(k\),否则去遍历右子树,将查询的排名减去左子树的大小加\(1\)。
否则直接遍历左子树即可。

int get_k(int x)
{
    int u=root;
    while(u!=0)
    {
        if(tr[tr[u].left].siz>=x) u=tr[u].left;
        else if(tr[tr[u].left].siz+1==x) return tr[u].num;
        else x=x-tr[tr[u].left].siz-1,u=tr[u].right;
    }
}

[HNOI2012]永无乡
需要利用并查集和 \(splay\) 的启发式合并
对于两棵 \(splay\) 的合并,遍历一棵树中的全部节点挨个插入即可。

void merge(int x,int y)//将以x为根的splay里的所有节点 遍历并插入到 以y为根的splay中
{
    if(tr[x].left!=0) merge(tr[x].left,y);
    if(tr[x].right!=0) merge(tr[x].right,y);
    insert(tr[x].v,tr[x].id,y);
}

[NOI2005] 维护数列
操作大杂烩 + \(5K\) 代码
对于区间求和操作,我们在每个节点上维护一个 \(sum\) 后,可以用一个类似的操作:先找到区间的前一个数 \(L\) 和后一个数 \(R\),将 \(L\) 转到根节点,再将 \(R\) 转到 \(L\) 的下面,此时求和的区间就是 \(R\) 的左子树,直接输出 \(sum\) 即可。
对于求最大子段和操作,它只可能是三种情况:左子树的最大子段和、右子树的最大子段和、左子树的最大后缀和加上右子树的最大前缀和加上这个节点的值,取一个 \(max\) 即可。但此时我们每个节点上还要再维护最大前缀和和最大后缀和,最大前缀和的维护就是它的左子树的总和加上左子树的值加上右子树的最大前缀和,最大后缀和的维护类似。

struct node{
    int left,right,fa,v,siz;
    int rev,same,sum,ms,ls,rs;//是否翻转 是否相同 和 最大子段和 最大前缀和 最大后缀和
    void init(int vv,int pp)//初始化
    {
        fa=pp,v=vv;
        left=right=0;
        rev=same=0;
        siz=1;
        sum=ms=v;
        ls=rs=max(v,0);
    }
} tr[N];
void pushup(int u)
{
    tr[u].siz=tr[tr[u].left].siz+tr[tr[u].right].siz+1;
    tr[u].sum=tr[tr[u].left].sum+tr[tr[u].right].sum+tr[u].v;
    tr[u].ls=max(tr[tr[u].left].ls,tr[tr[u].left].sum+tr[u].v+tr[tr[u].right].ls);
    tr[u].rs=max(tr[tr[u].right].rs,tr[tr[u].right].sum+tr[u].v+tr[tr[u].left].rs);
    tr[u].ms=max(tr[tr[u].left].rs+tr[u].v+tr[tr[u].right].ls,max(tr[tr[u].left].ms,tr[tr[u].right].ms));
}

此题为了防止空间爆炸的问题,也使用了内存回收的技巧,将删掉的点的编号放入一个回收站中,当插入另一个点的时候从回收站里取编号即可。


2022.12.22 ~ 2022.12.24 树套树学习
树套树,顾名思义就是两种数据结构的融合,一般码量较长。

练习题目
【模板】二逼平衡树(树套树)
操作有:查询区间内一个数的排名、查询区间内排名为 \(k\) 的数、单点修改、求区间内一个数的前驱和后继。
就是线段树套平衡树,线段树维护区间,每个线段树区间内的 \(splay\) 维护区间里的数。
此处不想过多解释了,直接贴代码吧,比较容易理解。(就是太难调了)

#include <iostream>
using namespace std;
const int N=2e6+9,INF=2147483647;
struct node{
    int left,right,siz,fa,v;
    void init(int father,int vv)
    {
        fa=father,v=vv;
        siz=1;
    }
} tr[N];//平衡树
int n,T,L[N],R[N],root[N],idx,a[N];//线段树数组,root表示这个线段树区间内平衡树的根节点
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void pushup(int k)
{
    tr[k].siz=tr[tr[k].left].siz+tr[tr[k].right].siz+1;
}
void rotate(int x)
{
    int y=tr[x].fa,z=tr[y].fa;
    if(tr[z].right==y) tr[z].right=x;
    else tr[z].left=x;
    tr[x].fa=z;
    if(tr[y].left==x) tr[y].left=tr[x].right,tr[tr[x].right].fa=y,tr[x].right=y,tr[y].fa=x;
    else tr[y].right=tr[x].left,tr[tr[x].left].fa=y,tr[x].left=y,tr[y].fa=x;
    pushup(y),pushup(x);
}
void splay(int &root,int x,int k)
{
    
    while(tr[x].fa!=k)
    {
        int y=tr[x].fa,z=tr[y].fa;
        if(z!=k)
        {
            if((tr[z].right==y) ^ (tr[y].right==x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    if(k==0) root=x;
}
void insert(int &root,int x)
{
    int u=root,p=0;
    while(u!=0)
    {
        p=u;
        if(x>tr[u].v) u=tr[u].right;
        else u=tr[u].left;
    }
    u=++idx;
    if(p!=0)
    {
        if(x>tr[p].v) tr[p].right=u;
        else tr[p].left=u;
    }
    tr[u].init(p,x);
    splay(root,u,0);
}
int get_k(int root,int x)//返回小于x的数的个数
{
    int u=root,ans=0;
    while(u!=0)
    {
        if(tr[u].v<x) ans=ans+tr[tr[u].left].siz+1,u=tr[u].right;
        else u=tr[u].left;
    }
    return ans;
}
void update(int &root,int x,int y)//删去x,插入y
{
    int u=root;
    while(u!=0)
    {
        if(tr[u].v==x) break;
        if(x>tr[u].v) u=tr[u].right;
        else u=tr[u].left;
    }
    splay(root,u,0);
    int l=tr[u].left,r=tr[u].right;
    while(tr[l].right!=0) l=tr[l].right;
    while(tr[r].left!=0) r=tr[r].left;
    splay(root,l,0),splay(root,r,l);
    tr[r].left=0;
    pushup(r),pushup(l);
    insert(root,y);
}
int get_pre(int root,int x)//求小于x的最大数
{
    int u=root,ans=-INF;
    while(u!=0)
    {
        if(x>tr[u].v) ans=max(ans,tr[u].v),u=tr[u].right;
        else u=tr[u].left;
    }
    return ans;
}
int get_suc(int root,int x)//求大于x的最小数
{
    int u=root,ans=INF;
    while(u!=0)
    {
        if(x>=tr[u].v) u=tr[u].right;
        else ans=min(ans,tr[u].v),u=tr[u].left;
    }
    return ans;
}
void build(int k,int l,int r)
{
    L[k]=l,R[k]=r;
    insert(root[k],-INF);
    insert(root[k],INF);
    for(int i=l;i<=r;i++) insert(root[k],a[i]);
    if(l==r) return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
}
int query(int k,int l,int r,int x)//求x的排名
{
    if(l<=L[k] && R[k]<=r) return get_k(root[k],x)-1;
    int mid=(L[k]+R[k])>>1,ans=0;
    if(l<=mid) ans=ans+query(k<<1,l,r,x);
    if(mid<r) ans=ans+query(k<<1|1,l,r,x);
    return ans;
}
void upd(int k,int pos,int x)
{
    update(root[k],a[pos],x);//在平衡树中修改
    if(L[k]==R[k]) return;
    int mid=(L[k]+R[k])>>1;
    if(pos<=mid) upd(k<<1,pos,x);
    else upd(k<<1|1,pos,x);
}
int query_pre(int k,int l,int r,int x)
{
    if(l<=L[k] && R[k]<=r) return get_pre(root[k],x);
    int mid=(L[k]+R[k])>>1,ans=-INF;
    if(l<=mid) ans=max(ans,query_pre(k<<1,l,r,x));
    if(mid<r) ans=max(ans,query_pre(k<<1|1,l,r,x));
    return ans;
}
int query_suc(int k,int l,int r,int x)
{
    if(l<=L[k] && R[k]<=r) return get_suc(root[k],x);
    int mid=(L[k]+R[k])>>1,ans=INF;
    if(l<=mid) ans=min(ans,query_suc(k<<1,l,r,x));
    if(mid<r) ans=min(ans,query_suc(k<<1|1,l,r,x));
    return ans;
}
int main()
{
    n=read(),T=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    while(T--)
    {
        int opt=read();
        if(opt==1)//给值查询排名
        {
            int a=read(),b=read(),x=read();
            printf("%d\n",query(1,a,b,x)+1);
        }
        else if(opt==2)//给排名查询值
        {
            int a=read(),b=read(),k=read();
            int l=0,r=1e8;
            while(l<r)//二分
            {
                int mid=(l+r+1)>>1;
                if(query(1,a,b,mid)+1<=k) l=mid;
                else r=mid-1;
            }
            printf("%d\n",l);
        }
        else if(opt==3)//插入操作
        {
            int pos=read(),x=read();
            upd(1,pos,x);
            a[pos]=x;
        }
        else if(opt==4)//求小于x的最大数
        {
            int a=read(),b=read(),x=read();
            printf("%d\n",query_pre(1,a,b,x));
        }
        else//求大于x的最小数
        {
            int a=read(),b=read(),x=read();
            printf("%d\n",query_suc(1,a,b,x));
        }
    }
    return 0;
}

完结撒花!

标签:24,right,int,18,tr,fa,root,2022.12,left
From: https://www.cnblogs.com/LYT0122/p/17003577.html

相关文章

  • AT_jag2018summer_day2_a 10^N+7 题解
    题目传送门题目大意有三个非负整数$x,y,z$,找到符合以下条件的最小非负整数\(n\);$n\{\rm\mod}\10^1+7\=\x$$n\{\rm\mod}\10^2+7\=\y$$n\{\rm\mo......
  • AT_mujin_pc_2018_b セキュリティ 题解
    题目传送门题目大意房间原有\(A\)人,+表示进来一个人,-表示出去一个人;求是否有一个时间,房间内的人数为\(0\)。解题思路按题意进行模拟:首先判断\(A\)是否等于零,......
  • P1868 饥饿的奶牛
    P1868饥饿的奶牛题意:有\(N\)个区间,每个区间\(x,y\)表示提供的$s\simy$共\(y-x+1\)堆牧草,可以选择任意区间,但是选的区间不能有重复部分。求最多可以获得......
  • 芯片ADS9224R的FPGA驱动实现
    ADS9224R这款芯片是德州仪器(TI)的一款SARADC,笔者写这芯片IP核的时候大概有段时间了,这款ADC采集芯片挺复杂的。笔者当时对写axi4_lite的IP核还不是很熟悉,就接下了含有......
  • 【软硬件环境配置】ubuntu20.04系统安装VMware虚拟机和ubuntu18.04
    前言操作步骤1. ​​下载虚拟机​​;下载最新版本的VmwareworkstationPro17;​​WindowsVM|WorkstationPro|VMware​​2.下载​​ubuntu18.04镜像​​;​​UbuntuRe......
  • x210-2022-12-24
    1、SD卡使用的是32G小卡,因为开发板是大卡卡槽,所以在小卡外加了一个大卡卡套,这张卡原来一直是用于串口屏烧录工程文件的,因为烧录串口屏时有容量要求,所以只分区了16G出来使用......
  • 新版以太坊Ethereum库ethersV5.0配合后端Golang1.18实时链接区块链钱包(Metamask/Okc)
    区块链去中心化思想无处不在,比如最近使用个体抗原自检替代大规模的中心化核酸检测,就是去中心化思想的落地实践,避免了大规模聚集导致的交叉感染,提高了检测效率,本次我们使用E......
  • UVA12459 Bees' ancestors 题解
    题目传送门题目大意雌蜂有一个父亲一个母亲,而雄蜂只有母亲。计算出Willy的祖先中,哪一代有多少祖先。解题思路已知Willy为雄蜂,从Willy开始向前推:有一个母亲(1);......
  • AT_pakencamp_2018_day2_a ひふみ (Hihumi) 题解
    题目传送门题目大意从\(1\)到\(N\)数数的时候,会数几个整数呢(除123外)?解题思路如果\(N\)小于123,就不会数到123,所以数了\(N\)次。否则,就会数到123,所以数的......
  • 使用nmcli命令配置rhel8.0系统的网络,要求IP地址为192.168.10.XX/24,网关为192.168.10.2
    ifconfig或 ip-a #查看网络nmcli     ​c  #查看链接nmcil    ​connectiondeleteens160  #删除ens160网卡链接nmcil    ​connection......