牛半仙的妹子序列
題目
思路
不妨把題目中的 “魅力值” 叫做 ⟨ 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 li≤j≤i≤rj
這裡肯定不會有相等的情況,所以也可以寫成嚴格不等號。這玩意兒就比較好處理了,按照 l i l_i li 排序後,每次加入所有 l i ≤ j l_i\le j li≤j ,樹狀陣列實現 [ 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;
}
相關文章
- 【10.22 牛客普及(三)】 牛半仙的妹子gcd 題解GC
- 7.29考試總結(NOIP模擬27)[牛半仙的妹子圖·Tree·序列]
- 【牛客網機試】和為S的連續正數序列
- 牛客題霸 [括號序列] C++題解/答案C++
- 內涵妹子
- 高仿富途牛牛-元件化(六)-炒雞牛逼的佈局記憶功能(序列化和反序列化)元件化
- 滬漂程式媛妹子的一天...
- 泳裝妹子果然棒
- 最高的牛
- 隨機看妹子_這是不可能的隨機
- Scrapy框架爬取海量妹子圖框架
- Python3--妹子圖實戰Python
- Python Selenium+PhantomJs 爬煎蛋妹子圖PythonJS
- scrapy 也能爬取妹子圖?(5)
- 1014: 牛生牛問題
- Node.js爬取妹子圖-crawler爬蟲的使用Node.js爬蟲
- 穩定的牛分配
- 畢業一年左右的前端妹子面經總結前端
- 給妹子講python-S01E03元組的使用Python
- 教你用Python爬取妹子圖APPPythonAPP
- 給隔壁的妹子講『一個SQL語句是如何執行的?』SQL
- Hive的序列化/反序列化(SerDe)Hive
- python的序列化和反序列化Python
- Java的序列化和反序列化Java
- Java的序列化與反序列化Java
- 途牛的疫情生死關
- python 中的序列Python
- 牛說
- 牛客
- 爬蟲福利----妹子圖網MM批量下載爬蟲
- 布隆,牛逼!布穀鳥,牛逼!
- Java中的序列化與反序列化Java
- PHP的序列化和反序列化入門PHP
- 你能找到心儀的妹子嗎?- 時間複雜度進階時間複雜度
- Acwing4244牛的比賽
- .NET物件的XML序列化和反序列化物件XML
- flutter-簡單實現找妹子自定義viewFlutterView
- 怎麼捕捉牛股?捕“牛股”掌握三技巧