近期刷題小結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);
}

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

題意
方法
心得
代碼

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