1. 程式人生 > 其它 >C. Tokitsukaze and Strange Inequality_字首和dp+特殊列舉

C. Tokitsukaze and Strange Inequality_字首和dp+特殊列舉

C. Tokitsukaze and Strange Inequality

題目大意

給一個數列,找四個下標[a,b,c,d],滿足a<b<c<d。找出滿足以下條件的種數。
a_a<a_c && a_b>a_d

思路和程式碼

我一開始做了一個O(n3 logn)的方法,有點笨。

具體就是列舉ac和b再二分d。

void solve(){//錯誤程式碼!!!
	int n ;
	cin >> n ;
	vct<int> a(n + 1 , 0) ;
	
	rep(i , 1 , n) cin >> a[i] ;
	
	vct<vct<int> > grt(n + 1) ;//i後面比ai大的
	vct<vct<int> > sml(n + 1) ;//i後面比ai小的
	
	rep(i , 1 , n)
	rep(j , i + 1 , n){
		if(a[j] > a[i]) grt[i].pb(j) ;
		else sml[i].pb(j) ;
	}
	
	ll ans = 0 ;
	
	rep(i , 1 , n){
		
		for(int j : grt[i]){
			
			rep(u , i + 1 , j - 1){
				int id = upper_bound(sml[u].begin() , sml[u].end() , j) - sml[u].begin() + 1 ;
				ans += sml[u].size() - id + 1 ;
			}
			
		}
		
	}
	cout << ans << "\n" ;
}//錯誤程式碼!!!

這樣做的複雜度很高。所以換一種方法。

將題目轉化為【1,b】中比a[c]小的乘上【c,n】中比a[c]小的。於是我們就可以去列舉b和c

void solve3(){
	int n ;
	cin >> n ;
	vct<ll> a(n + 1 , 0) ;
	rep(i , 1 , n) cin >> a[i] ;
	
	vct<vct<int> > lft(n + 1 , vct<int> (n + 1 , 0)) ;
	vct<vct<int> > rit(n + 1 , vct<int> (n + 1 , 0)) ;
	
	rep(i , 1 , n){
		ll num = 0 ;
		rep(jl , 1 , i - 1){//[1,jl]中比ai小的 
			num += a[jl] < a[i] ;
			lft[i][jl] = 1LL * num ;
		}
		
		num = 0 ;
		rep(jr , i + 1 , n){//[i+1,jr]中比ai小的 
			num += a[i] > a[jr] ;
			rit[i][jr] = 1LL * num ;
		}
	}
	
	ll ans = 0 ;
	
	rep(i , 1 , n)
	rep(j , i + 1 , n){
		ans += 1LL * (rit[i][n] - rit[i][j]) * (lft[j][i - 1]) ;
        //([i+1,n]中比ai小的 - [i+1,j]中比ai小的)* [1,i-1]中比aj小的
	}
	cout << ans << "\n" ;
	
}

當然以上程式碼不太好理解點就是其rit和lft的定義不太一致。具體看程式碼註釋。

小結

結合dp思想,列舉中間點轉化問題。

這是一個很好的題目

Update2022/5/9

昨晚打完比賽有點神志不清了,隨便寫了點就下機,現在來補充一些...

題目就是要求出下圖的四元點對數量

考慮列舉中間點b和c。構造前(後)綴和dp陣列lft和rit 。

1)rit[i,j]表示在[j,n]範圍內比ai小的數量

2)rit[i,j]表示在[j,n]範圍內比ai小的數量

所以最後的答案就是lft[c,b-1]*rit[b,c+1]在n2列舉b和c時求和。

void solve(){
	cin >> n ;
	vct<int> a(n + 1 , 0) ;
	
	get1(a , 1 , n) ;
	vct<vct<int> > lft(n + 2 , vct<int>(n + 2 , 0)) ;
	vct<vct<int> > rit(n + 2 , vct<int>(n + 2 , 0)) ;
	
	rep(i , 1 , n)
	rep(j , 1 , i - 1){
		lft[i][j] = lft[i][j - 1] + (a[i] > a[j] ? 1 : 0) ;
	}//lft[i,j]表示[1,j]中比ai小的數量 
	
	rep(i , 1 , n)
	drep(j , i + 1 , n){
		rit[i][j] = rit[i][j + 1] + (a[i] > a[j] ? 1 : 0) ;
	}//rit[i,j]表示[j,n]中比ai小的數量 

	ll ans = 0 ;
	rep(i , 1 , n - 1)
	rep(j , i + 1 , n)
	ans += 1LL * lft[j][i - 1] * rit[i][j + 1] ;
	
	cout << ans << "\n" ;
	
}//code_by_tyrii 

看一下上面那個紅綠配的圖就很明瞭了~