近期刷题小结10.14-10.19

P3380 【模板】二逼平衡树(树套树)

题意

在这里插入图片描述

方法

线段树套平衡树
查询k的排名:把每个区间的排名加起来O(logn2)O(log^2_n)
查询排名为k:不能相加,只能二分了O(logn3)O(log^3_n)
修改:把包含这个数的区间对应的二叉树都修改了O(logn2)O(log^2_n)
查询前驱:将每个区间的前驱取maxO(logn2)O(log^2_n)
查询后继:将每个区间的后继取minO(logn2)O(log^2_n)
时间复杂度O(nlogn3)O(n*log^3_n)
注意一下细节

心得

结构体方便

代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+4,inf=2147483647;
int n,m,a[N];
namespace treap{
	struct bala{
		int w,siz,num,wei,ch[2];
	}t[N*20];
	int tot=0;
	inline int newnode(int w){
		t[++tot].w=w;
		t[tot].wei=rand();
		t[tot].num=t[tot].siz=1;
		t[tot].ch[0]=t[tot].ch[1]=0;
		return tot;
	}
	inline void pushup(int p){
		t[p].siz=t[t[p].ch[1]].siz+t[t[p].ch[0]].siz+t[p].num;
	}
	inline void rotate(int &p,int d){
		int y=t[p].ch[d];
		t[p].ch[d]=t[y].ch[d^1];
		t[y].ch[d^1]=p;
		pushup(p);pushup(y);
		p=y;
	}
	inline void insert(int &p,int w){
		if(!p)p=newnode(w);
		else if(t[p].w==w)++t[p].num;
		else if(t[p].w>w){
			insert(t[p].ch[0],w);
			if(t[t[p].ch[0]].wei>t[p].wei)rotate(p,0);
		}
		else{
			insert(t[p].ch[1],w);
			if(t[t[p].ch[1]].wei>t[p].wei)rotate(p,1);
		}
		pushup(p);
	}
	inline void delet(int &p,int w){
		if(t[p].w>w)delet(t[p].ch[0],w);
		else if(t[p].w<w)delet(t[p].ch[1],w);
		else{
			if(t[p].num>1)t[p].num--;
			else if(!t[p].ch[0]&&!t[p].ch[1])p=0;
			else if(!t[p].ch[0]){
				rotate(p,1);
				delet(t[p].ch[0],w);
			} 
			else if(!t[p].ch[1]){
				rotate(p,0);
				delet(t[p].ch[1],w);
			}
			else{
				if(t[t[p].ch[0]].wei>t[t[p].ch[1]].wei){
					rotate(p,0);
					delet(t[p].ch[1],w);
				}
				else{
					rotate(p,1);
					delet(t[p].ch[0],w);
				}
			}
		}
		if(p)pushup(p);
	} 
	inline int queryrank(int p,int k){//+1
		if(!p)return 0;
		if(t[p].w>k)return queryrank(t[p].ch[0],k);
		if(t[p].w==k)return t[t[p].ch[0]].siz;
		return t[t[p].ch[0]].siz+t[p].num+queryrank(t[p].ch[1],k);
	}
	inline int querypre(int p,int k){
		if(!p)return -inf;
		if(k<=t[p].w)return querypre(t[p].ch[0],k);
		return max(t[p].w,querypre(t[p].ch[1],k));
	}
	inline int querysuf(int p,int k){
		if(!p)return inf;
		if(t[p].w<=k)return querysuf(t[p].ch[1],k);
		return min(t[p].w,querysuf(t[p].ch[0],k));
	}
}
namespace seg{
	#define lc (p<<1)
	#define rc (p<<1|1)
	int rt[N<<2];
	inline void build(int p,int l,int r){
		for(int i=l;i<=r;i++)//!!
			treap::insert(rt[p],a[i]);
		if(l==r)return;
		int mid=l+r>>1;
		build(lc,l,mid);
		build(rc,mid+1,r);
	}
	inline void modify(int p,int l,int r,int x,int y){
		treap::delet(rt[p],a[x]);
		treap::insert(rt[p],y);
		if(l==r)return;
		int mid=l+r>>1;
		if(x<=mid)modify(lc,l,mid,x,y);
		else modify(rc,mid+1,r,x,y);
	}
	inline int queryrank(int p,int l,int r,int L,int R,int k){
		if(L<=l&&r<=R)return treap::queryrank(rt[p],k);
		int mid=l+r>>1,ret=0;
		if(L<=mid)ret+=queryrank(lc,l,mid,L,R,k);
		if(mid<R)ret+=queryrank(rc,mid+1,r,L,R,k);
		return ret;
	}
	inline int querynum(int u,int v,int k){//不能像queryrank一样累加,故二分
		int l=0,r=1e8,mid;
		while(l<r){
			mid=l+r+1>>1;
			if(queryrank(1,1,n,u,v,mid)<k)l=mid;
			else r=mid-1;
		} 
		return r;
	}
	inline int querypre(int p,int l,int r,int L,int R,int k){
		if(L<=l&&r<=R)return treap::querypre(rt[p],k);
		int mid=l+r>>1,ret=-inf;
		if(L<=mid)ret=max(ret,querypre(lc,l,mid,L,R,k));
		if(mid<R)ret=max(ret,querypre(rc,mid+1,r,L,R,k));
		return ret;
	}
	inline int querysuf(int p,int l,int r,int L,int R,int k){
		if(L<=l&&r<=R)return treap::querysuf(rt[p],k);
		int mid=l+r>>1,ret=inf;
		if(L<=mid)ret=min(ret,querysuf(lc,l,mid,L,R,k));
		if(mid<R)ret=min(ret,querysuf(rc,mid+1,r,L,R,k));
		return ret;
	}
}
int main(){
	srand(time(0));
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	seg::build(1,1,n);
	for(int i=1,op,x,y,l,r,k;i<=m;i++){
		op=read();
		switch(op){
			case 1:l=read();r=read();k=read();printf("%d\n",seg::queryrank(1,1,n,l,r,k)+1);break;
			case 2:l=read();r=read();k=read();printf("%d\n",seg::querynum(l,r,k));break;
			case 3:x=read();y=read();seg::modify(1,1,n,x,y);a[x]=y;break;
			case 4:l=read();r=read();k=read();printf("%d\n",seg::querypre(1,1,n,l,r,k));break;
			case 5:l=read();r=read();k=read();printf("%d\n",seg::querysuf(1,1,n,l,r,k));break;
		}
	}
	return 0;
}

[CQOI2011]动态逆序对

题意

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

方法

线段树
我们只需知道删除元素的前面有几个比他大的,后面有几个比他小的,就可以算出减少的逆序对,可是序列要变啊
先用树状数组求出一开始的逆序对,算有几个数比自己大时,算的是区间,很容易想到可以用主席树,每次我们把删除的节点加入主席树,计算贡献时减去他们就好了

代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define ll long long
const int N=1e5+4;
int n,m,a[N],pre[N],suf[N],pos[N];
ll ans=0;
namespace bit{
	int t[N];
	inline void add(int x,int v){
		for(;x<=n;x+=x&-x)t[x]+=v;
	}
	inline int ask(int x){
		int ret=0;
		for(;x;x-=x&-x)ret+=t[x];
		return ret;
	}
	inline void clear(){
		memset(t,0,sizeof(t));
	}
}
namespace segment{
	struct tree{
		ll sum;
		int ls,rs;
	}t[N*60];
	int rt[N],tot=0;
	inline void insert(int &p,int l,int r,int x){
		if(!p)p=++tot;
		++t[p].sum;
		if(l==r)return;
		int mid=l+r>>1;
		if(x<=mid)insert(t[p].ls,l,mid,x);
		else insert(t[p].rs,mid+1,r,x); 
	}
	inline ll query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return t[p].sum;
		int mid=l+r>>1;
		ll ret=0;
		if(L<=mid)ret=query(t[p].ls,l,mid,L,R);
		if(mid<R)ret+=query(t[p].rs,mid+1,r,L,R);
		return ret;
	}
	inline ll find(int p1,int p2,int l,int r){
		if(l>r)return 0;
		ll ret=0;
		for(;p2;p2-=p2&-p2)
			ret+=query(rt[p2],1,n,l,r);
		for(;p1;p1-=p1&-p1)
			ret-=query(rt[p1],1,n,l,r);
		return ret;
	}
	inline void modify(int p,int x){
		for(;p<=n;p+=p&-p)insert(rt[p],1,n,x);
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		pos[a[i]]=i;
		pre[i]=bit::ask(n-a[i]);
		bit::add(n-a[i]+1,1);
	}
	bit::clear();
	for(int i=n;i;i--){
		suf[i]=bit::ask(a[i]-1);
		ans+=suf[i];
		bit::add(a[i],1);
	}
	for(int i=1,x,p;i<=m;i++){
		x=read();p=pos[x];
		printf("%lld\n",ans);
		ans-=pre[p]+suf[p]-segment::find(0,p,x+1,n)-segment::find(p,n,1,x-1);
		segment::modify(p,x);
		//加入删除的元素,计算贡献时就减去这些已删除的数 
	}
	return 0;
}

P3591 [POI2015]ODW

题意

给定一棵n个点的树,树上每条边的长度都为1,第i个点的权值为a[i]。Byteasar想要走遍这整棵树,他会按照某个1到n的全排列b走n-1次,第i次他会从b[i]点走到b[i+1]点,并且这一次的步伐大小为c[i]。对于一次行走,假设起点为x,终点为y,步伐为k,那么Byteasar会从x开始,每步往前走k步,如果最后不足k步就能到达y,那么他会一步走到y。请帮助Byteasar统计出每一次行走时经过的所有点的权值和。

方法

暴力数据结构——分块
如果步伐比分的块大小大,我们暴力
否则
b[i][j]b[i][j]表示,从根节点到当前节点的路径上,经过该节点,每jj个节点经过一个节点的权值和,预处理之后我们就可以用分块做了
时间复杂度O(nn)O(n\sqrt{n})

心得

分块好难写啊,动不动就写错,代码能力还是太弱了

代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=5e4+4,S=225;
struct edge{
	int v,nxt;
}e[N<<1];
int first[N],cnt=0;
inline void add(int u,int v){
	e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;
}
int n,a[N],b[N],C[N],fa[N][20],dep[N],s[S+1][N];
inline void dfs1(int x){
	for(int i=1;(1<<i)<=dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=first[x],v;i;i=e[i].nxt){
		v=e[i].v;
		if(v==fa[x][0])continue;
		fa[v][0]=x;
		dep[v]=dep[x]+1;
		dfs1(v);
	}
}
inline int LCA(int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	int tmp=dep[a]-dep[b];
	for(int i=0;i<20;i++)
		if((1<<i)&tmp)a=fa[a][i];
	if(a==b)return a;
	for(int i=19;i>=0;i--)
		if(fa[a][i]!=fa[b][i]){
			a=fa[a][i];b=fa[b][i];
		}
	return fa[a][0];
}
inline int getfa(int a,int x){
	for(int i=0;i<20;i++)
		if((1<<i)&x)a=fa[a][i];
	return a;
}
inline void dfs2(int x){
//s[i]表示根节点到i的路径上,每c个点取一个,且i恰好被取到(根不一定被取到)
	int u=fa[x][0];
	for(int i=1;i<=S;i++){
		s[i][x]=s[i][u]+a[x];
		u=fa[u][0];//
	}
	for(int i=first[x],v;i;i=e[i].nxt){
		v=e[i].v;
		if(v!=fa[x][0])dfs2(v);
	}
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1,u,v;i<n;i++){
		u=read();v=read();
		add(u,v);add(v,u);
	}
	for(int i=1;i<=n;i++)b[i]=read();
	for(int i=1;i<n;i++)C[i]=read();
	dep[1]=1;dfs1(1);
	dfs2(1);
	for(int i=1,u,v,c,lca,ans,flca,fv,c1,c2;i<n;i++){
		u=b[i];v=b[i+1];c=C[i];
		lca=LCA(u,v);
		if(c==1){
			printf("%d\n",s[1][u]+s[1][v]-s[1][lca]-s[1][fa[lca][0]]);
			continue;
		}
//		c1=(dep[u]-dep[lca])%c;
//		c2=(dep[v]-dep[lca]+c1)%c;
//		fv=getfa(lca,c2);
		if(c<=S){
			ans=s[c][u];
			c1=(dep[u]-dep[lca])%c;
			if(!c1)c1=c;//不含lca 
			for(int i=19;~i;i--)
				if(dep[fa[u][i]]-dep[lca]>=c1)u=fa[u][i];
			ans+=a[u]-s[c][u];
			if(dep[u]+dep[v]-(dep[lca]<<1)>=c){//含lca 
				c2=c-dep[u]+dep[lca];
				u=v;
				for(int i=19;~i;i--)
					if(dep[fa[u][i]]-dep[lca]>=c2)u=fa[u][i];//
				c1=(dep[v]-dep[u])%c;
				if(c1)ans+=a[v];
				v=getfa(v,c1);
				ans+=s[c][v]-s[c][u]+a[u];
			}
			else ans+=a[v];
			printf("%d\n",ans);
			continue;
//			flca=getfa(lca,c-c1);
//			ans=s[c][u]-s[c][flca];
//			cout<<ans<<" ";
//			flca=getfa(lca,c1);
//			ans+=s[c][fv]-s[c][flca];
//			printf("%d\n",ans);
//			continue;
		}
		ans=0;
		while(dep[u]-dep[lca]>c){
			ans+=a[u];u=getfa(u,c);
		}
		ans+=a[u];
		if(dep[u]+dep[v]-(dep[lca]<<1)>=c){
			int c1=c-dep[u]+dep[lca];
			u=v;
			for(int i=16;~i;i--)
				if(dep[fa[u][i]]-dep[lca]>=c1)u=fa[u][i];
			c1=(dep[v]-dep[u])%c;
			if(c1)ans+=a[v];
			v=getfa(v,c1);
			while(dep[v]-dep[u]>=c){
				ans+=a[v];v=getfa(v,c);
			}
			ans+=a[v];
		}
		else ans+=a[v];
		printf("%d\n",ans);
//		ans=0;
//		while(dep[u]>=dep[lca]){
//			ans+=a[u];
//			u=getfa(u,c);
//		}
//		while(dep[fv]>dep[lca]){
//			ans+=a[fv];
//			fv=getfa(fv,c);
//		}
	}
	return (0-0);
}

P5357 【模板】AC自动机(二次加强版)

题意

在这里插入图片描述

方法

建AC自动机
有个优化,每次匹配到时,并不用跳failfail指针,只用在该节点上记值,最后我们用failfail建树,dfsdfs一遍,求出答案

心得

玄学优化

代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+4,M=2e6+4;
int n,a[N][27],fail[N],siz[N],cnt=1,ed[N];
char t[N],s[M];
inline void getfail(){
	fail[1]=0;
	queue<int>q;
	for(int i=0;i<26;i++)a[0][i]=1;
	q.push(1);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=0,u;i<26;i++){
			u=a[x][i];
			if(!u){
				a[x][i]=a[fail[x]][i];
				continue;
			}
			fail[u]=a[fail[x]][i];
			q.push(u);
		}
	}
}
struct edge{
	int v,nxt;
}e[N];
int first[N],tot=0;
inline void add(int u,int v){
	e[++tot].v=v;e[tot].nxt=first[u];first[u]=tot;
}
inline void dfs(int x){
	for(int i=first[x],v;i;i=e[i].nxt){
		v=e[i].v;
		dfs(v);
		siz[x]+=siz[v];//统计反了 
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1,len,p,c;i<=n;i++){
		scanf("%s",t+1);
		len=strlen(t+1);
		p=1;
		for(int j=1;j<=len;j++){
			c=t[j]-'a';
			if(!a[p][c])a[p][c]=++cnt;
			p=a[p][c];
		}
		ed[i]=p;//结尾节点 
	}
	getfail();
	scanf("%s",s+1);
	for(int i=1,p=1,len=strlen(s+1);i<=len;i++){
		p=a[p][s[i]-'a'];
		siz[p]++;//每个子串遍历次数 
	}
	for(int i=1;i<=cnt;i++)
		add(fail[i],i);
	dfs(1);//累计次数 
	for(int i=1;i<=n;i++)
		printf("%d\n",siz[ed[i]]);
	return (0-0);
}

P3812 【模板】线性基

题意

给定n个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。

方法

线性基是一个数的集合,并且每个序列都拥有至少一个线性基
三(四)大性质:

  1. 原序列里面的任意一个数都可以由线性基里面的一些数异或得到
  2. 线性基里面的任意一些数异或起来都不能得到0
  3. 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的

推荐学习
可以用线性基解决,贪心选最大值

心得

真神奇

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
int n,ans=0,a[51],p[101];
inline void solve(int x){
	for(int i=50;i>=0;i--){
		if(!(x>>i))continue;
		if(!p[i]){p[i]=x;break;}
		x^=p[i];
	}
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++)
		solve(a[i]=read());
	for(int i=50;i>=0;i--)
		if((ans^p[i])>ans)ans^=p[i];
	cout<<ans;
	return 0;
}

CF126B Password

题意

你需要找到既是S的前缀又是S的后缀同时又在S中间出现过的最长子串

现在给你字符串S,你需要找到满足上述要求的子串T

方法

扩展KMP,不过实际好像叫ZalgorithmZ-algorithm
思想和manachemanache差不多
在这里插入图片描述
用法大概就是字符串匹配吧。模式串+’$’+文本串是一个很好的选择。

求出zz数组后,如何保证前缀也是后缀呢?z[i]==n-i.直观理解上就是以这一位为开始的串有n-i位与前缀相同。显而易见这说的就是后缀和前缀相等。那如何保证这一个串在中间也出现过呢?遍历的过程中记录一个z[i]的最大值maxx,若这个maxx>=n-i,则说明前面至少出现过不短于他的一个和前缀相同的串

心得

最后答案的求得还不是很能求出,好要加油!!!!!!!!!!!!!

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+4;
int n,fail[N],z[N];
char s[N];
inline void Z_algorithm(){
	for(int i=2,l=1,r=1,k;i<=n;i++){
		if(i>r){
			l=r=i;
			while(r<=n&&s[r-l+1]==s[r])r++;
			z[i]=r-l;r--;
		}
		else{
			k=i-l+1;
			if(z[k]<r-i+1)z[i]=z[k];
			else{
				l=i;
				while(r<=n&&s[r-l+1]==s[r])r++;
				z[i]=r-l;r--;
			}
		}
	}
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	Z_algorithm();
	int mx=0,pos=0;
	for(int i=2;i<=n;i++){
		if(z[i]==n-i+1&&mx>=n-i+1){pos=i;break;}//妙啊!! 
		mx=max(mx,z[i]);
	}
	if(!pos)printf("Just a legend");
	else for(int i=1;i<=n-pos+1;i++)putchar(s[i]);
	return (0-0);
}

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码

题意
方法
心得
代码
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章