NAIVE-01

[文章目录]

1.在相同deep的节点上二分时间戳

用vector存储相同deep的节点,做一个链表,然后通过at(i),函数二分就好了

引申1:可以在dfs序上找到一个节点的子树的时间戳的范围,然后在相同deep深度的链表上二分。那么就能得到关于一个节点的子树相同深度节点。时间O(log n),预处理O(n)。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int m,ans;
vector<int> a;
vector <int> :: iterator it;
int main()
{
	for(int i=1;i<=101;i+=7) a.push_back(i);
	for(it=a.begin();it!=a.end();it++) printf("%d ",*it);
	scanf("%d",&m);
	int l=0,r=a.size(),mid;
	while(l<r)
	{
		mid=l+r>>1;
		if(a.at(mid)<=m) ans=a.at(mid),l=mid+1;
		else r=mid;
	}
	printf("%d",ans);
	return 0;
}

其实可以直接将bfs序放到一个连续数组里,记录端点然后开始二分。

2.<cmath>之神器 log (double )

首先这个函数返回的是double。然后呢?又有什么卵用???

比如,给你一坨正整数,让你有一定的规则的瞎乘,找到乘积最大的取模输出。

但是,一堆数乘起来会爆啊,取模也会在比较的时候炸掉啊。

嗯,这是就需要log()函数了。

我们知道那个log(a*b)=log(a)+log(b)。另外log函数还是递增的,所以,直接加和,和大的就是乘积大的。

不过答案呢?我们并不能计算一个e^double,所以只能记录最优解的计算路径,然后再累乘取模。

3.zkw线段树

其实挺简单的(不过后面50多页的差分树我根本不会啊QAQ,哭死)。不过还是能做挺多东西的。

思想:类似手写heap,找到点编号的关系,建立一颗树,自底向上的搞,实现非递归。

下面为无区间修改的zkw

建树:

就是开够一个底层空间为n+2的树,将元素扔进底层,再从底向上更新参数。

底层第一个元素的编号为M,底层的位置个数为M,根为1(不过zkw都是自底向上,跟根毛关系都没有)

void build()
{
    for(M=1;M<=n+1;M<<=1);//至少有n+2个位置,因为后面会有开区间查询,修改
    for(int i=M+1;i<=n+M;i++) sum[i]=mx[i]=mi[i]=read(),size[i]=1;//一定要空一位,为后面维护极值的开区间做准备
    for(int i=M-1;i;i--) pushup(i);//还是从M-1开始,因为M位没有值。 
}
传递(pushup):(因题而异)
mx[pos]=max(mx[pos<<1],mx[pos<<1|1]);
mi[pos]=min(mi[pos<<1],mi[pos<<1|1]);
sum[pos]=sum[pos<<1]+sum[pos<<1|1];
size[pos]=size[pos<<1]+size[pos<<1|1];
//其实还有很多,应需搞一搞
查询:传进来的l,r是原数列的编号。也就是要求输出原数列[l,r]的解,写法都类似。
int query_mx(int l,int r)//闭区间
{
    int re=-1<<30;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)//变成开区间
    {
        if(~l&1) re=max(re,mx[l^1]);
        if(r&1) re=max(re,mx[r^1]);
    }
    return re;
}
int query_mi(int l,int r)
{
    int re=1<<30;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) re=min(re,mi[l^1]);
        if(r&1) re=min(re,mi[r^1]);
    }
    return re;
}
int query_sum(int l,int r)
{
    int re=0;
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) re+=sum[l^1];
        if(r&1) re+=sum[r^1];
    }
    return re;
}
单点修改:
void fix_mx(int id,int now)//把第id个数改成now
{
    for(mx[id+=M]=now,id>>=1;id;id>>=1) push_mx_up(id);
}
void fix_mi(int id,int now)
{
    for(mi[id+=M]=now,id>>=1;id;id>>=1) push_mi_up(id);
}
void fix_sum(int id,int now)
{
    for(sum[id+=M]=now,id>>=1;id;id>>=1) push_sum_up(id);
}
单点查询和区间没啥不同的吧???变简单了好吧。

下面为带有区间修改的zkw

额...lazy标记是噩梦啊(线段树的精髓啊)...下面就给个区间修改,区间查询和的好吧。

建树:一样,只是多来了个lazy[]
传递:一样,(仅限于加和)
修改:噩梦开始。。。

由于要修改小区间,然而包含小区间的大区间的参数也会变,所以我们需要自底向上一路更新参数。还有就是,由于标记永久化 ,所以原来的标记可能并未下传到小区间,向上更新的时候需要累计当前节点代表区间的lazy值,所以还需要维护一个siz值。

void Fix(LL l,LL r,LL w)
{
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) lazy[l^1]+=w,t[l^1]+=siz[l^1]*w;//将节点直接处理好,查询的时候就不用再累加当前的lazy了
        if(r&1) lazy[r^1]+=w,t[r^1]+=siz[r^1]*w;
        t[l>>1]=t[l]+t[l^1]+siz[l>>1]*lazy[l>>1];t[r>>1]=t[r]+t[r^1]+siz[r>>1]*lazy[r>>1];//计算lazy值,一定要写
    }
    while(l>>=1) t[l]=t[l<<1]+t[l<<1|1]+lazy[l]*siz[l];
    //由于内部是提前更新,所以while(l>>=1)先右移,后更新
}
查询:区间和
LL query(LL l,LL r)
{
    if(r<l) return 0ll;
    LL re=0,lsz=0,rsz=0;//lsz表示从左边上来的节点个数,用于累加lazy的值,rsz同理
    for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1)
    {
        re+=(lazy[l]*lsz+lazy[r]*rsz);//累加下面小区间没有更新的部分
        if(~l&1) re+=t[l^1],lsz+=siz[l^1];//由于t[]的值已经是更新好的了,所以不需要累加当前区间的lazy[]
        if(r&1) re+=t[r^1],rsz+=siz[r^1];//需要累计的节点个数
    }
    re+=(lazy[l]*lsz+lazy[r]*rsz);//两边节点有共同父亲,lazy不同
    lsz+=rsz;//siz累加
    while(l>>=1) re+=lazy[l]*lsz;//继续向上,while(l>>=1) 先右移,后计算
    return re;
}

看着zkw还挺简单的,但是需要考虑的还挺多的。需要拍熟。到考场上如果写错一个地方就GG了。

原谅我太NAIVE

发表评论

邮箱地址不会被公开。 必填项已用*标注