牛半仙的妹子序列

OneInDark發表於2020-10-31

題目

傳送門 to nowcoder

思路

不妨把題目中的 “魅力值” 叫做 ⟨ a ⟩ \langle a\rangle a

d p \tt dp dp ,轉移條件比較苛刻。如果 f ( j ) f(j) f(j) 能轉移到 f ( i ) f(i) f(i) ,需要滿足:只關注不超過 a i a_i ai 的數 時, a j a_j aj [ j , i ] [j,i] [j,i] 中最小的。

這個條件一看就是 c d q \tt cdq cdq 分治。只要看到 “只關注某值在某區間內” 的條件,往往都是如此。不妨看看御神渡,其轉移條件是 a a a 值不相同,等價於一個字首、一個字尾,也是 c d q \tt cdq cdq 分治。

此時肯定是按照 a a a 作為區間劃分依據。怎麼算交叉的貢獻呢?同樣的,與 御神渡 中求凸包沒啥區別。只看 [ l , m ] [l,m] [l,m] 中的 a a a ,求出每個值右邊第一個比自己大的,稱為 r j r_j rj ;只看 ( m , r ] (m,r] (m,r] 中的 a a a ,求出每個值左邊第一個比自己大的,稱為 l i l_i li 。那麼轉移的充要條件變成

l i ≤ j ≤ i ≤ r j l_i\le j\le i\le r_j lijirj

這裡肯定不會有相等的情況,所以也可以寫成嚴格不等號。這玩意兒就比較好處理了,按照 l i l_i li 排序後,每次加入所有 l i ≤ j l_i\le j lij ,樹狀陣列實現 [ j , r j ] [j,r_j] [j,rj] 的區間加。複雜度 O ( n log ⁡ 2 n ) \mathcal O(n\log^2n) O(nlog2n)

程式碼

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 200010;
const int infty = (1<<30)-1;
const int Mod = 998244353;
int n; // though alone, still necessary
struct Node{
	int a, id, l, r, dp;
	Node(){ dp = 0; }
	bool operator < (const Node &t) const {
		return id < t.id; // 按照下標排序
	}
};
Node node[MaxN];
class BIT{
	int c[MaxN];
public:
	void modify(int x,int v){
		for(int i=x; i<=n; i+=(i&-i))
			c[i] = (c[i]+v)%Mod;
	}
	int query(int x){
		int res = 0;
		for(int i=x; i; i-=(i&-i))
			res = (res+c[i])%Mod;
		return res;
	}
	void clear(int x){
		for(int i=x; i<=n; i+=(i&-i))
			c[i] = 0;
	}
};
BIT xyx;

vector< int > sta;
void getInfo(int l,int r){
	sort(node+l,node+r+1);
	sta.clear(); // good habit
	sta.push_back(0); // be bottom
	for(int i=l; i<=r; ++i){
		while(!sta.empty() &&
		node[sta.back()].a > node[i].a)
			sta.pop_back();
		node[i].l = node[sta.back()].id;
		sta.push_back(i);
	}
	sta.clear(); // essential
	sta.push_back(n+1); // be bottom
	for(int i=r; i>=l; --i){
		while(!sta.empty() &&
		node[i].a > node[sta.back()].a)
			sta.pop_back();
		node[i].r = node[sta.back()].id;
		sta.push_back(i);
	}
}
bool cmp(const Node &x,const Node &y){
	return x.l < y.l; // 按照 l 排序
}
bool cdq(const Node &x,const Node &y){
	return x.a < y.a; // 按照 a 值排序
}
void solve(int l,int r){
	if(l == r) return ; // nothing to do
	int m = (l+r)>>1;
	solve(l,m); // 中序遍歷
	getInfo(l,m), getInfo(m+1,r);
	sort(node+m+1,node+r+1,cmp);
	sort(node+l,node+m+1); // 下標排序
	for(int i=r,j=m; i>m; --i){
		int zxy = node[i].l;
		while(j >= l && zxy < node[j].id){
			int x = node[j].dp; // 加入
			xyx.modify(node[j].r,Mod-x);
			xyx.modify(node[j].id+1,x);
			-- j; // 非常重要!
		}
		node[i].dp += xyx.query(node[i].id);
		node[i].dp %= Mod; // 小心點!
	}
	for(int i=l; i<=m; ++i){
		xyx.clear(node[i].r);
		xyx.clear(node[i].id+1);
	}
	sort(node+m+1,node+r+1,cdq);
	solve(m+1,r); // 繼續遞迴右邊
}

int main(){
	n = readint();
	for(int i=1,now=n+1; i<=n; ++i){
		node[i].id = i;
		node[i].a = readint();
		if(node[i].a < now){
			node[i].dp = 1;
			now = node[i].a;
		}
	}
	node[0].id = node[0].a = 0;
	node[n+1].id = node[n+1].a = n+1;
	sort(node+1,node+n+1,cdq);
	solve(1,n); // 兩個邊界都是實點
	sort(node+1,node+n+1); // 還原成原序列!
	int ans = 0; // sy orz. xyx orz.
	for(int i=n,now=0; i; --i)
		if(node[i].a > now){
			ans = (node[i].dp+ans)%Mod;
			now = node[i].a;
		}
	printf("%d\n",ans);
	return 0;
}

相關文章