LOJ3228「USACO 2019.12 Platinum」Tree Depth


題意

求所有\(n\)元逆序對數為\(k\)的排列所對應的笛卡爾樹中(每次選區間最小連在父親下,再分為左右兩部分遞歸),求每個位置在所有樹中的深度和
\(1 \le n \le 300\)

思路

是設\(f_k\)表示逆序對數為\(k\)\(n\)排列的數量,那么其生成函數:

\[F(x)=\prod_{i=0}^{n-1}\sum_{j=0}^{i}x^j \]

(大概就是看一下在前面所有數中的位置)

然后再來看需要求的東西,深度和即為有多少個父親。\(i\)\(j\)的父親需要滿足\(i\)\([i,j]\)\([j,i]\)的最小值。考慮生成函數中的順序其實可以調換,我們不一定要按從前到后的順序來考慮,也可以先把\([l,r]\)區間拿出來考慮內部的逆序對,再從后往前考慮\([1,l]\)考慮對\([i+1,l]\)中數的貢獻,在從前往后考慮\([r+1,n]\)的數對\([1,i-1]\)中的貢獻,對於每一個式子,都能夠構造出與之對應的排列,也就是說,只要你是挨着構造的,且大小關系不產生矛盾,那一“項”對應那一個位置都沒問題。

然后單獨考慮\(i,j\)的父子關系,若\(i\)\([i,j]\)中的最小值,則\(i\)會貢獻\(0\)個逆序對,此時\(i\)\(j\)的父親。若\(j\)\([i,j]\)中的最小值,則\(j\)會貢獻\(len-1(j-i)\)個逆序對。為了方便,我們把\(i,j\)這個位置貢獻的逆序對數目拿出來單獨考慮,即\(1+x+ \dots +x^{len}\)那一項除掉。然后對於前者,有\(f[k]\)個排列,后者有\(f[k-(len-1)]\)

來自蒟蒻zjy的瞎掰,看的人不會很多,有耐心看的人可能就我一個,你們如果真想研究可以去zsy大佬那兒看看

#include <bits/stdc++.h>
int f[300*160],n,k,N,mu,ans[305];
void reduce(int &x) {x+=x>>31&mu;}
void cheng(int k){
	for (int i=N;i>=k;i--) reduce(f[i]-=f[i-k]);
	for (int i=1;i<=N;i++) reduce(f[i]+=f[i-1]-mu);
} 
void chu(int k){
	for (int i=N;i;i--) reduce(f[i]-=f[i-1]);
	for (int i=k;i<=N;i++) reduce(f[i]+=f[i-k]-mu);
}
int main(){
	scanf("%d%d%d",&n,&k,&mu);
	N=n*(n-1)/2;
	f[0]=1;
	for (int i=2;i<=n;i++) cheng(i);
	for (int i=1;i<=n;i++) ans[i]=f[k];//自己的深度
	for (int i=2;i<=n;i++){
		chu(i);//此段區間加入的貢獻下面重新算
		for (int j=1;j+i-1<=n;j++){
			if (k-(i-1)>=0) reduce(ans[j]+=f[k-(i-1)]-mu);//j+i-1最小 
			reduce(ans[j+i-1]+=f[k]-mu);//j最小 
		}
		cheng(i); 
	} 
	for (int i=1;i<=n;i++) printf("%d ",ans[i]); 
} 

后記

大型補blog現場(補不完了)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM