奇技淫巧and板子

本文将记录一些奇技淫巧和板子(最基础的也有),先从今天开始写,以前的找个时间再补上

杂项

(2020.4.29之前写的)没有系统的提纲,很杂乱,没什么看点,因此以后的会整理一下

求第\(k\)大的数

应用快速排序思想(\(O(n)\)),平常都是\(O(nlogn)\)
基于快排的思想,在每一层递归中,随机选取一个数做基准时,统计出大于基准值的数的个数\(cnt\),如果\(k<=cnt\)就在左半段(比基准数大)中寻找第\(k\)大的数,反之则在右半段寻找第\(k-cnt\)大的数

复杂度证明:

因为每次只进入了左右两段的任意一个,则在平均情况之下复杂度为:\(n+\frac{n}{2}+\frac{n}{3}+.....+1=O(n)\)

求长度不小于L的子段使之和最大

ST表

预处理

void st_prework() {
	for(int i=1; i<=n; ++i) f[i][0]=a[i];
	for(int j=1; j<=21; ++j)
		for(int i=1; i+(1<<j)-1<=n; ++i)
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

查询

int query(int l,int r) {
	int res=-9999;
	int k=0;
	while((1<<(k+1)<=r-l+1)) k++;
	res=max(f[l][k],f[r-(1<<k)+1][k]);
	return res;
}

\(O(1)\)实现能查询栈中最小元素

开两个栈,a存原始数据,b存历史上每个时刻的最小值
举个例子
a:9 2 1 5 3 0 2 <-
b:9 2 1 1 1 0 0 <-
每次插入元素x时,a插入x,b插入min(top(b),x),弹出时一起弹,询问时输出top(b)

二分

之前用的二分太傻比了,需要考虑的东西太多(可是我没有脑子),学长就教了一个咕咕咕的二分

int l=0,r=1e18;//取个大一点的数
while(l<=r)
{
    int mid=(l+r)>>1;
    if(check(mid)) r=mid-1,ans=mid;
    else l=mid+1;
}
cout<<ans;
double l=0,r=1e18;//取个大一点的数
while(r-l<=eps)
{
    double mid=(l+r)/2;
    if(check(mid)) r=mid,ans=mid;
    else l=mid;
}
cout<<ans;

树和图的深度优先遍历和广度优先遍历

//dfs
void dfs(int x)
{
	visit[x]=1;
	for(int i=head[x];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(visit[to]) continue;
		dfs(to);
	}
}

//bfs
void bfs()
{
	queue<int>q;
	q.push(1);
	d[1]=1;//层数 
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=e[i].Next)
		{
			int to=e[i].v;
			if(visit[to]) continue;
			d[to]=d[u]+1;
			q.push(to);
		}
	}
 } 

树的dfs序

dfs序的特点:

每个节点x编号在序列中出现两次,而且以这两次出现位置为端点的闭区间就是以x为根的子树的DFS序

void dfs(int x)
{
        a[++len]=x;//记录dfs序
	visit[x]=1;
	for(int i=head[x];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(visit[to]) continue;
		dfs(to);
	}
        a[++len]=x;
}

求树的重心

void dfs(int x)
{
	int max_part=0;//删除掉x后最大子树大小 
	size[x]=1;//子树x的大小 
	visit[x]=1;
	for(int i=head[x];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(visit[to]) continue;
		dfs(to);
		size[x]+=size[y];
		max_part=max(max_part,size[y]);
	}
	max_part=max(max_part,n-size[x]);//n为整颗树的节点数目
	if(max_part<ans)
	{
		ans=max_part;//记录重心对应的max_part值 
		pos=x;//记录重心编号 
	 } 
}

图的联通块划分

void dfs(int x)
{
	visit[x]=cnt;
	for(int i=head[x];i;i=e[i].Next)
	{
		int to=e[i].v;
		if(visit[to]) continue;
		dfs(to);
	}
} 
for(int i=1;i<=n;++i)
	if(!visit[i]) cnt++,dfs(i);

拓扑排序

void topsort()
{
	queue<int>q;
	for(int i=1;i<=n;++i) 
		if(du[i]==0) q.push(i);//du在add时记录 
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		a[++len]=u;//记录拓扑序 
		for(int i=head[u];i;i=e[i].Next)
		{
			int to=e[i].v;
			if(--du[to]==0) q.push(to);
		}
	}
 } 

使用负数下表

int * z=y+50
z[50]-->y[0]

treap平衡树

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2019;
const int INF=(1<<30);
int sum=0,R=0;
int size[N],v[N],num[N],rd[N],son[N][2];
void pushup(int p) {
	size[p]=size[son[p][0]]+size[son[p][1]]+num[p];
}
void rotate(int &p,int d) {
	int k=son[p][d^1];
	son[p][d^1]=son[k][d];
	son[k][d]=p;
	pushup(p),pushup(k);
	p=k;
}
void ins(int &p,int x) {
	if(!p) {
		p=++sum;
		size[p]=num[p]=1;
		v[p]=x,rd[p]=rand();
		return ;
	}
	if(v[p]==x) {
		num[p]++,size[p]++;
		return ;
	}
	int d=(x>v[p]);
	ins(son[p][d],x);
	if(rd[p]<rd[son[p][d]]) rotate(p,d^1);
	pushup(p);
}
void del(int &p,int x) {
	if(!p) return ;
	if(x<v[p]) del(son[p][0],x);
	else if(x>v[p]) del(son[p][1],x);
	else {
		if(!son[p][0]&&!son[p][1]) {
			num[p]--,size[p]--;
			if(num[p]==0) p=0;
		} else if(son[p][0]&&!son[p][1]) {
			rotate(p,1);
			del(son[p][1],x);
		} else if(!son[p][0]&&son[p][1]) {
			rotate(p,0);
			del(son[p][0],x);
		} else if(son[p][0]&&son[p][1]) {
			int d=(rd[son[p][0]]>rd[son[p][1]]);
			rotate(p,d);
			del(son[p][d],x);
		}
	}
	pushup(p);
}
int paiming(int p,int x)
{
	if(!p) return 0;
	if(v[p]==x) return size[son[p][0]]+1;
	if(v[p]<x) return size[son[p][0]]+num[p]+paiming(son[p][1],x);
	if(v[p]>x) return paiming(son[p][0],x);
}
int find(int p,int x)
{
	if(!p) return 0;
	if(size[son[p][0]]>=x) return find(son[p][0],x);
	else if(size[son[p][0]]+num[p]<x) return find(son[p][1],x-num[p]-size[son[p][0]]);
	else return v[p];
}
int pre(int p,int x)
{
	if(!p) return -INF;
	if(v[p]>=x) return pre(son[p][0],x);
	else return max(v[p],pre(son[p][1],x));
}
int Next(int p,int x)
{
	if(!p) return INF;
	if(v[p]<=x) return Next(son[p][1],x);
	else return min(v[p],Next(son[p][0],x));
}
int main() {
	int n;
	scanf("%d",&n);
	for (int i=0; i<n; ++i) {
		int opt,x;
		scanf("%d%d",&opt,&x);
		if (opt==1) ins(R,x);
		else if (opt==2) del(R,x);
		else if (opt==3) printf("%d\n",paiming(R,x));
		else if (opt==4) printf("%d\n",find(R,x));
		else if (opt==5) printf("%d\n",pre(R,x));
		else if (opt==6) printf("%d\n",Next(R,x));
	}
	return 0;
}

fhq treap

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2019;
struct node {
	int l,r;
	int val,key;
	int size;
} fhq[N];
int T;
int cnt,root;
int nownode(int val) {
	fhq[++cnt].val=val;
	fhq[cnt].size=1;
	fhq[cnt].key=rand();
	return cnt;
}
void update(int p) {
	fhq[p].size=fhq[fhq[p].l].size+fhq[fhq[p].r].size+1;
}
void spilt(int now,int val,int &x,int &y) {//x和y是拆分后两树的根节点
	if(!now) x=y=0;
	else {
		if(fhq[now].val<=val) {
			x=now;
			spilt(fhq[now].r,val,fhq[now].r,y);//拆分右子树,因为右子树中可能还存在比val小的节点
		} else {
			y=now;
			spilt(fhq[now].l,val,x,fhq[now].l);//复读机
		}
		update(now);
	}
}
int merge(int x,int y) {
	if(!x||!y) return x+y;
	if(fhq[x].key>fhq[y].key) { //维护根的性质
		//根据堆的性质得到的新树x在y上面,而且根据二叉搜索树的性质y一定在x下面,所有y在x右下
		fhq[x].r=merge(fhq[x].r,y);
		update(x);
		return x;
	} else { //复读机
		fhq[y].l=merge(x,fhq[y].l);
		update(y);
		return y;
	}
}
int x,y,z;
void ins(int val) {
	spilt(root,val,x,y);
	root=merge(merge(x,nownode(val)),y);//这里忘给root负值了 
}
void del(int val) {
	spilt(root,val,x,z);
	spilt(x,val-1,x,y);
	y=merge(fhq[y].l,fhq[y].r);
	root=merge(merge(x,y),z);
}
int getrank(int val) {
	spilt(root,val-1,x,y);
	int ret=fhq[x].size+1;
	root=merge(x,y);//先return再合并 
	return ret;
}
int getnum(int pm) {
	int now=root;
	while(now) {
		if(fhq[fhq[now].l].size+1==pm) break;
		else if(fhq[fhq[now].l].size>=pm) now=fhq[now].l;
		else {
			pm-=fhq[fhq[now].l].size+1;
			now=fhq[now].r;
		}
	}
	return fhq[now].val;
}
int pre(int val) {
	spilt(root,val-1,x,y);
	int now=x;
	while(fhq[now].r) now=fhq[now].r;
	int ret=fhq[now].val;
	root=merge(x,y);
	return ret;
}
int Next(int val) {
	spilt(root,val,x,y);
	int now=y;
	while(fhq[now].l) now=fhq[now].l;
	int ret=fhq[now].val;
	root=merge(x,y);
	return ret;
}
int main() {
	srand((unsigned)time(0));
	scanf("%d",&T);
	while(T--) {
		int opt,x;
		scanf("%d%d",&opt,&x);
		if(opt==1) ins(x);
		if(opt==2) del(x);
		if(opt==3) cout<<getrank(x)<<'\n';
		if(opt==4) cout<<getnum(x)<<'\n';
		if(opt==5) cout<<pre(x)<<'\n';
		if(opt==6) cout<<Next(x)<<'\n';
	}
	return 0;
}

可能是splay

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2019;
const int INF=(1<<30);
int sum=0,R=0;
int size[N],v[N],num[N],rd[N],son[N][2];
void pushup(int p) {
	size[p]=size[son[p][0]]+size[son[p][1]]+num[p];
}
void rotate(int &p,int d) {
	int k=son[p][d^1];
	son[p][d^1]=son[k][d];
	son[k][d]=p;
	pushup(p),pushup(k);
	p=k;
}
void ins(int &p,int x) {
	if(!p) {
		p=++sum;
		size[p]=num[p]=1;
		v[p]=x,rd[p]=rand();
		return ;
	}
	if(v[p]==x) {
		num[p]++,size[p]++;
		return ;
	}
	int d=(x>v[p]);
	ins(son[p][d],x);
	if(rd[p]<rd[son[p][d]]) rotate(p,d^1);
	pushup(p);
}
void del(int &p,int x) {
	if(!p) return ;
	if(x<v[p]) del(son[p][0],x);
	else if(x>v[p]) del(son[p][1],x);
	else {
		if(!son[p][0]&&!son[p][1]) {
			num[p]--,size[p]--;
			if(num[p]==0) p=0;
		} else if(son[p][0]&&!son[p][1]) {
			rotate(p,1);
			del(son[p][1],x);
		} else if(!son[p][0]&&son[p][1]) {
			rotate(p,0);
			del(son[p][0],x);
		} else if(son[p][0]&&son[p][1]) {
			int d=(rd[son[p][0]]>rd[son[p][1]]);
			rotate(p,d);
			del(son[p][d],x);
		}
	}
	pushup(p);
}
int paiming(int p,int x)
{
	if(!p) return 0;
	if(v[p]==x) return size[son[p][0]]+1;
	if(v[p]<x) return size[son[p][0]]+num[p]+paiming(son[p][1],x);
	if(v[p]>x) return paiming(son[p][0],x);
}
int find(int p,int x)
{
	if(!p) return 0;
	if(size[son[p][0]]>=x) return find(son[p][0],x);
	else if(size[son[p][0]]+num[p]<x) return find(son[p][1],x-num[p]-size[son[p][0]]);
	else return v[p];
}
int pre(int p,int x)
{
	if(!p) return -INF;
	if(v[p]>=x) return pre(son[p][0],x);
	else return max(v[p],pre(son[p][1],x));
}
int Next(int p,int x)
{
	if(!p) return INF;
	if(v[p]<=x) return Next(son[p][1],x);
	else return min(v[p],Next(son[p][0],x));
}
int main() {
	int n;
	scanf("%d",&n);
	for (int i=0; i<n; ++i) {
		int opt,x;
		scanf("%d%d",&opt,&x);
		if (opt==1) ins(R,x);
		else if (opt==2) del(R,x);
		else if (opt==3) printf("%d\n",paiming(R,x));
		else if (opt==4) printf("%d\n",find(R,x));
		else if (opt==5) printf("%d\n",pre(R,x));
		else if (opt==6) printf("%d\n",Next(R,x));
	}
	return 0;
}

位运算

补码

\(c\)按位取反后结果为\(-1-c\)

一个小技巧

\[(a+y)^x=a^x (mod y) \]

例题

posted @ 2019-10-14 09:00  pyyyyyy  阅读(393)  评论(2编辑  收藏  举报