<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【學(xué)習(xí)筆記】笛卡爾樹

      前言

      本來早就該學(xué)笛卡爾樹了,但暑假打模擬賽就一直沒學(xué)成。于是就打算先不學(xué)了,結(jié)果又發(fā)現(xiàn)后面有個(gè)笛卡爾樹專題,只好來學(xué)學(xué)。

      定義

      笛卡爾樹是一棵二叉樹,每個(gè)點(diǎn)有一個(gè)鍵和一個(gè)值,鍵滿足堆的性質(zhì),值滿足二叉搜索樹的性質(zhì)。沒錯(cuò)當(dāng)鍵隨機(jī)時(shí),這就是個(gè) Treap。

      建樹

      如果值單調(diào)遞增,那么就可以線性建樹。具體地,維護(hù)整棵樹的右鏈。右鏈就是從根開始不停往右兒子走的鏈。因?yàn)橹颠f增所以新加的點(diǎn)一定會(huì)在右鏈中。假設(shè)新加入的點(diǎn) \(u\) 值為 \(k\) 鍵為 \(w\),記為 \((k,w)\),則從右鏈下端開始不斷向上比較,找到一個(gè) \(x\) 使它的鍵小于 \(w\)。那么 \(u\) 就成為 \(x\) 的右兒子,\(x\) 原本的右兒子就成為 \(u\) 的左兒子。
      放張圖更直觀(紅色方框即為右鏈):

      圖中的數(shù)字就是相應(yīng)的點(diǎn)的鍵。顯然用單調(diào)棧來維護(hù)右鏈就行了。
      核心代碼:

      for(int i=1;i<=n;i++){
      	while(top&&a[zhan[top]]>a[i]){
      		top--;
      	}
      	int &tmp=top?rs[zhan[top]]:rt;
      	ls[i]=tmp;
      	tmp=i;
      	zhan[++top]=i;
      }
      

      代碼中值為 \(i=1\dots n\),對(duì)應(yīng)的鍵為 \(a_i\)

      例題

      [luogu5854]笛卡爾樹模板
      按如上方式建樹即可。值得一提的是通過這道題可以發(fā)現(xiàn):當(dāng)鍵與值都互不相同時(shí),笛卡爾樹的形態(tài)是唯一的。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define read(x){\
      	char ch;\
      	int fu=1;\
      	while(!isdigit(ch=getchar()))\
      		fu-=(ch=='-')<<1;\
      	x=ch&15;\
      	while(isdigit(ch=getchar()))\
      		x=(x<<1)+(x<<3)+(ch&15);\
      	x*=fu;\
      }
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=1e7+5;
      int n,a[maxn],rt;
      int zhan[maxn],top;
      int ls[maxn],rs[maxn];
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	read(n);
      	for(int i=1;i<=n;i++){
      		read(a[i]);
      	}
      	for(int i=1;i<=n;i++){
      		while(top&&a[zhan[top]]>a[i]){
      			top--;
      		}
      		int &tmp=top?rs[zhan[top]]:rt;
      		ls[i]=tmp;
      		tmp=i;
      		zhan[++top]=i;
      	}
      	ll ans1=0,ans2=0;
      	for(int i=1;i<=n;i++){
      		ans1^=i*1ll*(ls[i]+1);
      		ans2^=i*1ll*(rs[i]+1);
      	}
      	printf("%lld %lld",ans1,ans2);
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      [TJOI2011] 樹的序
      觀察原樹的建樹方式,發(fā)現(xiàn)先加的一定在后加的上面。換句話說對(duì)于題中給出的 \(k_i\),下標(biāo) \(i\) 為鍵而 \(k_i\) 為值。因此先將 \(k\) 排序,用笛卡爾樹的方式線性建樹。
      然后我們要找到能建出相同樹的序列,必然也是從上向下建的,而我們又想要字典序最小,這還是一棵二叉搜索樹,因此必然要先輸出根,再遍歷左子樹,然后再遍歷右子樹。一個(gè) dfs 即可。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define read(x){\
      	char ch;\
      	int fu=1;\
      	while(!isdigit(ch=getchar()))\
      		fu-=(ch=='-')<<1;\
      	x=ch&15;\
      	while(isdigit(ch=getchar()))\
      		x=(x<<1)+(x<<3)+(ch&15);\
      	x*=fu;\
      }
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=1e5+5;
      int n,a[maxn],p[maxn];
      int rt,ls[maxn],rs[maxn];
      int top,zhan[maxn];
      il void dfs(int u){
      	if(!u){
      		return ;
      	}
      	printf("%d ",u);
      	dfs(ls[u]);
      	dfs(rs[u]);
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	read(n);
      	for(int i=1;i<=n;i++){
      		read(a[i]);
      		p[i]=i;
      	}
      	sort(p+1,p+n+1,[](const int &x,const int &y){return a[x]<a[y];});
      	for(int i=1;i<=n;i++){
      //		cout<<p[i]<<"\n";
      		while(top&&p[zhan[top]]>p[i]){
      			top--;
      		}
      		int &tmp=top?rs[zhan[top]]:rt;
      		ls[i]=tmp;
      		tmp=i;
      		zhan[++top]=i;
      	}
      //	for(int i=1;i<=n;i++){
      //		cout<<i<<" "<<ls[i]<<" "<<rs[i]<<"\n";
      //	}
      	dfs(rt);
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      [hdu6305]RMQ Similar Sequence
      首先,對(duì) \(A\) 數(shù)組建一個(gè)滿足大根堆性質(zhì)的笛卡爾樹。
      這時(shí)候你發(fā)現(xiàn),\(RMQ(A,l,r)\) 就是 \(l\)\(r\)\(lca\)。
      因此如果也類似地給 \(B\) 建出笛卡爾樹,這兩棵樹一定是相同的。因?yàn)?\(B\) 的和的期望是確定的,即 \(\frac{n}{2}\),因此只需計(jì)算一個(gè)笛卡爾樹與 \(A\) 的笛卡爾樹相同的概率就行了。
      顯然對(duì)于每棵子樹,只要根都是相同的,則樹就一定是相同的。設(shè) \(sz\) 為子樹大小,則概率為 \(\frac{1}{\prod{sz_i}}\)。因此答案為\(\frac{n}{2\prod{sz_i}}\)。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define read(x){\
      	char ch;\
      	int fu=1;\
      	while(!isdigit(ch=getchar()))\
      		fu-=(ch=='-')<<1;\
      	x=ch&15;\
      	while(isdigit(ch=getchar()))\
      		x=(x<<1)+(x<<3)+(ch&15);\
      	x*=fu;\
      }
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=1e6+5,mod=1e9+7;
      int T,n,a[maxn],rt;
      int zhan[maxn],top;
      int ls[maxn],rs[maxn];
      int inv[maxn],sz[maxn];
      il void dfs(int u){
      //	cout<<u<<"\n";
      	if(!u){
      		return ;
      	}
      	dfs(ls[u]);
      	dfs(rs[u]);
      	sz[u]=sz[ls[u]]+sz[rs[u]]+1;
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	inv[1]=1;
      	for(int i=2;i<=1e6;i++){
      		inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
      	}
      //	for(int i=1;i<=1e6;i++){
      //		cout<<i*1ll*inv[i]%mod<<"\n";
      //	}
      	read(T);
      	while(T--){
      		read(n);
      		top=rt=0;
      		for(int i=1;i<=n;i++){
      			read(a[i]);
      			while(top&&a[zhan[top]]<a[i]){
      				top--;
      			}
      			int &tmp=top?rs[zhan[top]]:rt;
      			ls[i]=tmp;
      			tmp=i;
      			zhan[++top]=i;
      		}
      		dfs(rt);
      		int ans=inv[2]*1ll*n%mod;
      		for(int i=1;i<=n;i++){
      			ans=ans*1ll*inv[sz[i]]%mod;
      		}
      		printf("%d\n",ans);
      		for(int i=1;i<=n;i++){
      			ls[i]=rs[i]=sz[i]=0;
      		}
      	}
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      [洛谷 P6453]PERIODNI
      考慮如果 \(a_i\) 很小,那 \(i\) 兩邊的高于 \(a_i\) 的部分就成了兩個(gè)獨(dú)立的子問題。因此可以建立小根堆笛卡爾樹,然后進(jìn)行樹形 DP。
      設(shè) \(f_{u,i}\) 表示 \(u\) 的子樹中高于 \(a_{fa_u}\) 的地方放了 \(i\) 個(gè)點(diǎn)的方案數(shù)。顯然答案為 \(f_{rt,m}\)。
      考慮轉(zhuǎn)移。首先是高于 \(a_u\) 的部分,顯然可以通過樹上背包轉(zhuǎn)移:

      \[f_{u,i}=\sum_{v\in son_u}\sum_{j=1}^{i}f_{u,i-j}\times f_{v,j} \]

      然后是 \((a_{fa_u},a_u]\) 的部分。設(shè)要放 \(i\) 個(gè),有 \(j\) 個(gè)在 \((a_{fa_u},a_u]\),剩下大于 \(a_u\),即要在剩下 \(sz_u-(i-j)\) 個(gè)節(jié)點(diǎn)、\(a_u-a_{fa_u}\) 個(gè)高度中選擇 \(j\) 個(gè)位置放點(diǎn)。于是有轉(zhuǎn)移方程:

      \[f_{u,i}=\sum_{j=1}^{i}f_{u,i-j}\times C_{a_u-a_{fa_u}}^{j}\times A_{sz_u-i+j}^{j} \]

      注意兩次轉(zhuǎn)移中都是小的更新大的,所以 \(i\) 要倒序遍歷。

      Code
      #include<bits/stdc++.h>
      #define ll long long
      #define il inline
      #define read(x){\
      	char ch;\
      	int fu=1;\
      	while(!isdigit(ch=getchar()))\
      		fu-=(ch=='-')<<1;\
      	x=ch&15;\
      	while(isdigit(ch=getchar()))\
      		x=(x<<1)+(x<<3)+(ch&15);\
      	x*=fu;\
      }
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=505,mod=1e9+7,maxm=1e6+5;
      int n,m,a[maxn],sz[maxn];
      int rt,top,zhan[maxn];
      int ls[maxn],rs[maxn];
      int f[maxn][maxn];
      int fac[maxm],inv[maxm];
      il int qpow(int x,int y){
      	int res=1;
      	while(y){
      		if(y&1){
      			res=res*1ll*x%mod;
      		}
      		x=x*1ll*x%mod,y>>=1;
      	}
      	return res;
      }
      il void init(int x){
      	fac[0]=1;
      	for(int i=1;i<=x;i++){
      		fac[i]=fac[i-1]*1ll*i%mod;
      	}
      	inv[x]=qpow(fac[x],mod-2);
      	for(int i=x;i;i--){
      		inv[i-1]=inv[i]*1ll*i%mod;
      	}
      }
      il int C(int x,int y){
      	if(x<y||y<0){
      		return 0;
      	}
      	return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
      }
      il int A(int x,int y){
      	if(x<y||y<0){
      		return 0;
      	}
      	return fac[x]*1ll*inv[x-y]%mod;
      }
      il void dfs(int u,int fa){
      	if(!u){
      		return ;
      	}
      	dfs(ls[u],u),dfs(rs[u],u);
      	sz[u]=sz[ls[u]]+sz[rs[u]]+1;
      	f[u][0]=1;
      	for(int i=min(m,sz[u]);i;i--){
      		for(int j=1;j<=min(i,sz[ls[u]]);j++){
      			f[u][i]=(f[u][i]+f[u][i-j]*1ll*f[ls[u]][j])%mod;
      		}
      	}
      	for(int i=min(m,sz[u]);i;i--){
      		for(int j=1;j<=min(i,sz[rs[u]]);j++){
      			f[u][i]=(f[u][i]+f[u][i-j]*1ll*f[rs[u]][j])%mod;
      		}
      	}
      	for(int i=min(m,sz[u]);i;i--){
      		for(int j=1;j<=i;j++){
      			f[u][i]=(f[u][i]+f[u][i-j]*1ll*C(a[u]-a[fa],j)%mod*A(sz[u]-i+j,j))%mod;
      		}
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      	read(n)read(m);
      	for(int i=1;i<=n;i++){
      		read(a[i]);
      		while(top&&a[zhan[top]]>a[i]){
      			top--;
      		}
      		int &tmp=top?rs[zhan[top]]:rt;
      		ls[i]=tmp;
      		tmp=zhan[++top]=i;
      	}
      	init(1e6);
      	dfs(rt,0);
      	printf("%d",f[rt][m]);
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      [hdu4125]Moles
      顯然我們找到原樹模 \(2\) 意義下的歐拉序,跑一個(gè) kmp 就好了。
      考慮如何建樹,這棵樹按 \(a\) 值滿足二叉搜索樹的性質(zhì),按下標(biāo)滿足小根堆的性質(zhì)。所以這就是一棵笛卡爾樹。線性建樹即可。時(shí)間復(fù)雜度 \(O(n\log n)\)

      Code
      #include<cstdio>
      #include<ctype.h>
      #include<algorithm>
      #include<iostream>
      #include<cstring>
      #define ll long long
      #define il inline
      #define read(x){\
      	char ch;\
      	int fu=1;\
      	while(!isdigit(ch=getchar()))\
      		fu-=(ch=='-')<<1;\
      	x=ch&15;\
      	while(isdigit(ch=getchar()))\
      		x=(x<<1)+(x<<3)+(ch&15);\
      	x*=fu;\
      }
      using namespace std;
      namespace asbt{
      namespace cplx{bool begin;}
      const int maxn=6e5+5;
      int T,n,zhan[maxn],nxt[maxn];
      int a[maxn],p[maxn],tot;
      int ls[maxn],rs[maxn];
      char s[maxn],t[maxn<<1]; 
      il void dfs(int u){
      	t[++tot]=u&1|48;
      	if(ls[u]){
      		dfs(ls[u]);
      		t[++tot]=u&1|48;
      	}
      	if(rs[u]){
      		dfs(rs[u]);
      		t[++tot]=u&1|48;
      	}
      }
      il void solve(){
      	read(n);
      	for(int i=1;i<=n;i++){
      		read(a[i]);
      		p[i]=i;
      	}
      	scanf(" %s",s+1);
      	int len=strlen(s+1);
      	int top=0,rt=0;
      	sort(p+1,p+n+1,[](const int &x,const int &y){return a[x]<a[y];});
      	for(int i=1;i<=n;i++){
      		while(top&&p[zhan[top]]>p[i]){
      			top--;
      		}
      		int &tmp=top?rs[zhan[top]]:rt;
      		ls[i]=tmp;
      		tmp=zhan[++top]=i;
      	}
      	tot=0;
      	dfs(rt);
      //	for(int i=1;i<=tot;i++){
      //		cout<<t[i];
      //	}
      //	puts("");
      	nxt[1]=0;
      	for(int i=2,j=0;i<=len;i++){
      		while(j&&s[j+1]!=s[i]){
      			j=nxt[j];
      		}
      		if(s[j+1]==s[i]){
      			j++;
      		}
      		nxt[i]=j;
      	}
      	int ans=0;
      	for(int i=1,j=0;i<=tot;i++){
      		while(j&&s[j+1]!=t[i]){
      			j=nxt[j];
      		}
      		if(s[j+1]==t[i]){
      			j++;
      		}
      		if(j==len){
      			ans++,j=nxt[j];
      		}
      	}
      	printf("%d\n",ans);
      	for(int i=1;i<=n;i++){
      		ls[i]=rs[i]=0;
      	}
      }
      namespace cplx{
      	bool end;
      	il double usdmem(){return (&begin-&end)/1048576.0;}
      }
      int main(){
      //	cout<<cplx::usdmem();
      	read(T);
      	for(int i=1;i<=T;i++){
      		printf("Case #%d: ",i);
      		solve();
      	}
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      [hdu6854]Kcats
      考慮 \(a\) 的實(shí)際含義,即為 \(p\) 的小根笛卡爾樹上 \(i\) 到根上權(quán)值 \(\le\) 的點(diǎn)數(shù)。于是可以進(jìn)行區(qū)間 dp。設(shè) \(f_{i,j,x}\) 表示 \([i,j]\) 的根的 \(a\) 值為 \(x\) 的方案數(shù)。于是有轉(zhuǎn)移:

      \[f_{i,j,a_k}\leftarrow {j-i\choose k-i}\times f_{i,k-1,a_k}\times f_{k+1,j,a_k+1} \]

      時(shí)間復(fù)雜度是 \(O(Tn^4)\) 的,但是能過。

      Code
      #include<cstdio>
      #include<iostream>
      #include<functional>
      #define ll long long
      #define il inline
      using namespace std;
      namespace asbt{
      int main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	const int mod=1e9+7;
      	function<int(int,int)>qpow=[](int x,int y)->int{
      		int res=1;
      		while(y){
      			if(y&1){
      				res=res*1ll*x%mod;
      			}
      			y>>=1,x=x*1ll*x%mod;
      		}
      		return res;
      	};
      	int *fac=new int[105](),*inv=new int[105]();
      	fac[0]=1;
      	for(int i=1;i<=100;i++){
      		fac[i]=fac[i-1]*1ll*i%mod;
      	}
      	inv[100]=qpow(fac[100],mod-2);
      	for(int i=100;i;i--){
      		inv[i-1]=inv[i]*1ll*i%mod;
      	}
      //	for(int i=0;i<=100;i++){
      //		cout<<fac[i]<<" "<<fac[i]*1ll*inv[i]%mod<<"\n";
      //	}
      	function<int(int,int)>C=[=](int x,int y)->int{
      		if(x<y||y<0){
      			return 0;
      		}
      		return fac[x]*1ll*inv[y]%mod*inv[x-y]%mod;
      	};
      	int *a=new int[105]();
      	int (*f)[105][105]=new int[105][105][105]();
      	int T;
      	cin>>T;
      	while(T--){
      		int n;
      		cin>>n;
      		for(int i=1;i<=n;i++){
      			cin>>a[i];
      		}
      		for(int i=1;i<=n;i++){
      			for(int j=1;j<=n;j++){
      				for(int k=1;k<=n;k++){
      					f[i][j][k]=0;
      				}
      			}
      		}
      		for(int len=1;len<=n;len++){
      			for(int i=1,j=len;j<=n;i++,j++){
      				for(int k=i,l,r;k<=j;k++){
      					if(~a[k]){
      						l=r=a[k];
      					}
      					else{
      						l=1,r=n;
      					}
      					for(int x=l;x<=r;x++){
      						(f[i][j][x]+=C(j-i,k-i)*1ll*(i<k?f[i][k-1][x]:1)%mod*(k<j?f[k+1][j][x+1]:1)%mod)%=mod;
      					}
      				}
      			}
      		}
      		cout<<f[1][n][1]<<"\n";
      	}
      	delete[] fac,inv,a,f;
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      啊上面這個(gè)因?yàn)?hdu 評(píng)測(cè)機(jī)太慢過不了,這告訴我們可以用楊輝三角時(shí)就用吧,還有不要用 new 定義數(shù)組。。。

      Code
      #include<cstdio>
      #include<iostream>
      #include<functional>
      #define ll long long
      #define il inline
      using namespace std;
      namespace asbt{
      const int mod=1e9+7;
      int C[105][105],a[105],f[105][105][105];
      int main(){
      	ios::sync_with_stdio(0),cin.tie(0);
      	for(int i=0;i<=100;i++){
      		C[i][0]=1;
      		for(int j=1;j<=i;j++){
      			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
      		}
      	}
      	int T;
      	cin>>T;
      	while(T--){
      		int n;
      		cin>>n;
      		for(int i=1;i<=n;i++){
      			cin>>a[i];
      		}
      		for(int i=1;i<=n;i++){
      			for(int j=1;j<=n;j++){
      				for(int k=1;k<=n;k++){
      					f[i][j][k]=0;
      				}
      			}
      		}
      		for(int len=1;len<=n;len++){
      			for(int i=1,j=len;j<=n;i++,j++){
      				for(int k=i,l,r;k<=j;k++){
      					if(~a[k]){
      						l=r=a[k];
      					}
      					else{
      						l=1,r=n;
      					}
      					for(int x=l;x<=r;x++){
      						(f[i][j][x]+=C[j-i][k-i]*1ll*(i<k?f[i][k-1][x]:1)%mod*(k<j?f[k+1][j][x+1]:1)%mod)%=mod;
      					}
      				}
      			}
      		}
      		cout<<f[1][n][1]<<"\n";
      	}
      	return 0;
      }
      }
      int main(){return asbt::main();}
      

      復(fù)雜度看起來假的離譜的 dp 如果常數(shù)小有可能就是真的了……

      posted @ 2025-01-05 22:00  zhangxy__hp  閱讀(82)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 在线中文字幕第一页| 亚洲成人高清av在线| 国产精品毛片av999999| 欧美激情内射喷水高潮| 九九热在线视频免费播放| 无码人妻丝袜在线视频| 呈贡县| 狠狠色丁香婷婷综合尤物| 最新精品国偷自产在线美女足| 人人人澡人人肉久久精品| 婷婷亚洲综合五月天小说| 欧美人成精品网站播放| 亚洲精品国产综合麻豆久久99| 人妻精品久久无码专区精东影业 | 免费观看欧美猛交视频黑人| 日本熟妇人妻一区二区三区| 亚洲一二三区精品与老人| 国产av最新一区二区| 一区二区亚洲人妻精品| 久久国产自拍一区二区三区| 国产偷窥熟女高潮精品视频| 亚洲一区二区三区四区| 亚洲人成网线在线播放VA| 国语精品国内自产视频| 国产精品二区中文字幕| 香港三级韩国三级日本三级| 日本一区二区三区在线 |观看| 亚洲欧洲精品成人久久曰| 中文字幕免费一二三区乱码| 亚洲欧美日韩综合久久久| 亚洲高潮喷水无码AV电影| 午夜DY888国产精品影院| 国产精品爽爽久久久久久| 国产欧亚州美日韩综合区| 国产精品高清一区二区三区| 国精品无码一区二区三区在线蜜臀| 99久久激情国产精品| 亚洲午夜av一区二区| 亚洲精品日本一区二区| 久久久国产乱子伦精品作者| 国产中文99视频在线观看|