t1
给出一张有n个点m条边的无向图,现在给出起点s和终点t,每个点还存在一个点权,用01表示,询问在起点带的最少的钱,到达终点的前不少于w,每到达一个点,如果当前点权为1,就减少钱1,否则减少钱\(\lceil \frac{w}{k} \rceil\),并且还要求输出字典序最小的路径,\(n\leq 5\times 10^5,m\leq 10^6\)。
解
法一:
显然刚开始的前,不妨设为\(e\),不好确定,而又具有单调性,因为e增大剩下的前肯定增大,否则减小,于是可以二分确定e,确定了e以后,现在问题就变成找出一条最优秀的路径,这显然是最短路问题,考虑优先队列bfs,因为钱是单调递减的,于是当前的状态能够到达的最优的状态,就是起点能够到达的最优状态,而对于字典序,只要考虑优先队列当中最小的相同的钱所在的点,取字典序最小那个,最终就可以保证路径字典序的最小(所以不能使用\(A^*\)),最终时间复杂度为\(nlog(n)^2\)。
参考代码
#include <iostream> #include <cstdio> #include <cstring> #define il inline #define ri register #define ll long long #define Size 500500 #define llmax 4611686018427387904 using namespace std; template<class free> struct heap{ free a[Size];int n; il void push(free x){ a[++n]=x;ri int p(n); while(p>1) if(a[p]<a[p>>1]) swap(a[p],a[p>>1]), p>>=1; else break; } il void pop(){ a[1]=a[n--];ri int p(1),s(2); while(s<=n){ if(s<n&&a[s+1]<a[s])++s; if(a[s]<a[p]) swap(a[s],a[p]), p=s,s<<=1; else break; } } }; struct pi{ ll x;int y,z; il bool operator<(const pi&a){ return x==a.x?y<a.y:x>a.x; } }; struct point{ point*next;int to; }*head[Size]; int s,t,w,k; heap<pi>H; int pre[Size]; bool a[Size],check[Size]; void print(int); il bool bfs(ll); template<class free> il void read(free&); il void link(int,int); int main(){ int n,m; read(n),read(m),read(k); for(int i(1);i<=n;++i)read(a[i]); for(int i(1),u,v;i<=m;++i) read(u),read(v),link(u,v),link(v,u); read(s),read(t),read(w);ll l(0),mid,r(llmax); while(l<=r){mid=l+r>>1; if(bfs(mid))l=mid+1; else r=mid-1; }printf("%lld\n",l); print(pre[t]),printf("%d",t); return 0; } void print(int x){ if(!x)return; print(pre[x]); printf("%d->",x); } il bool bfs(ll money){ memset(check,0,sizeof(check)); H.n=0,H.push({money,s,0});pi s; while(H.n){ s=H.a[1],H.pop();if(check[s.y])continue; check[s.y]=true,pre[s.y]=s.z;if(s.y==t)return s.x<w; for(point *i(head[s.y]);i!=NULL;i=i->next) if(!check[i->to])H.push( {s.x-(a[i->to]?1:(s.x+k-1)/k),i->to,s.y}); } } il void link(int u,int v){ head[u]=new point{head[u],v}; } template<class free> il void read(free &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
法二:
最短路问题,一定注意从起点出发和从终点出发是等价的,于是我们考虑从终点出发,显然终点只需要带w钱,然后不断倒推,求出到起点的最优解即可,代价是单调的,显然可以利用优先队列bfs解决问题。
现在关键是如何确定到达下一个点还剩多少钱,显然关键在于当前所在的点的权值恰好为0如何确定下一个点的钱,不妨设下一个点钱为x,当前钱为y,显然是解整除方程
\(y=x-\lceil \frac{x}{k} \rceil=x-\lfloor \frac{x+k-1}{k} \rfloor=x-(\frac{x+k-1}{k}-\{\frac{x+k-1}{k}\})=\)
\(x-(\frac{x+k-1}{k}-\frac{(x+k-1)\%(k-1)}{k})\Rightarrow x=\frac{ky}{k-1}+1-\frac{(x+k-1)\% (k-1)}{k-1}\)
此时容易知道\((x+k-1)\% (k-1)<k-1\),所以\(\frac{(x+k-1)\% (k-1)}{k-1}\in [0,1)\),所以式子的后半部分全部\(\in(0,1]\),而x必须为一个整数,所以\(\frac{ky}{k-1}+[0,1)=\lceil \frac{ky}{k-1} \rceil\)。
于是我们可以倒着优先队列bfs,这样可以在\(O(nlog(n))\)的时间内求出最优解,但是注意到无法输出最优的方案,但是我们晓得了要带的钱,可以顺着最短路,求出最小字典序。
参考代码:
#include <iostream> #include <cstdio> #include <cstring> #define il inline #define ri register #define ll long long #define Size 500500 using namespace std; template<class free> struct heap{ free a[Size];int n; il void push(free x){ a[++n]=x;ri int p(n); while(p>1) if(a[p]<a[p>>1]) swap(a[p],a[p>>1]), p>>=1; else break; } il void pop(){ a[1]=a[n--];ri int p(1),s(2); while(s<=n){ if(s<n&&a[s+1]<a[s])++s; if(a[s]<a[p]) swap(a[s],a[p]), p=s,s<<=1; else break; } } }; bool flag; struct pi{ ll x;int y,z; il bool operator<(const pi&a){ return (x==a.x?y<a.y:x<a.x)^flag; } }; struct point{ point*next;int to; }*head[Size]; il ll bfs1(); void print(int); il void bfs2(ll); int s,t,w,pre[Size],k; bool a[Size],check[Size]; template<class free> il void read(free&); il void link(int,int); int main(){ int n,m;read(n),read(m),read(k); for(int i(1);i<=n;++i)read(a[i]); for(int i(1),u,v;i<=m;++i) read(u),read(v),link(u,v),link(v,u); read(s),read(t),read(w);ll ans(bfs1()); printf("%lld\n",ans),bfs2(ans); return 0; } void print(int x){ if(pre[x])print(pre[x]),printf("%d->",pre[x]); } heap<pi>H; il void bfs2(ll x){ flag=true,memset(check,0,sizeof(check)); H.n=0,H.push({x,s,0});pi s; while(H.n){ s=H.a[1],H.pop();if(check[s.y])continue; check[s.y]=true,pre[s.y]=s.z; if(s.y==t)return print(t),(void)printf("%d",t); for(point*i(head[s.y]);i!=NULL;i=i->next){ if(check[i->to])continue; H.push({s.x-(a[i->to]?1: (s.x+k-1)/k),i->to,s.y}); } } } il ll bfs1(){ H.push({w,t});pi s; while(H.n){ s=H.a[1],H.pop();if(check[s.y])continue; check[s.y]=true;if(s.y==::s)return s.x; for(point *i(head[s.y]);i!=NULL;i=i->next){ if(check[i->to])continue; H.push({a[s.y]?s.x+1:(s.x*k+k-2)/(k-1),i->to}); } } } il void link(int u,int v){ head[u]=new point{head[u],v}; } template<class free> il void read(free &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
t2
给出一个有n个点m条边的无向图,有边权,已经给出,m只有两种取值即\(n-1\ and\ n\),定义一个点的权值为,从该点出发,随机选择一条边移动,走简单路径而且走到死的路径长度的数学期望,求点权的平均值,\(n\leq 10^6\)。
解
显然这是一个基环树问题,先考虑树的分数。
显然走到死,就是走到叶子节点,于是只要求出每个点为根节点到达叶子节点的数学期望即可,不妨设\(f_i\)表示从以i为根的子树中从i走到叶子节点的路径长度数学期望,显然有(定义\(w[i][j]\)为i,j间边长,deg[i]为i的点度,root为钦定的一个根节点)
\[f_i=\frac{\sum_{j\in son(i)}f_j+w[i][j]}{deg[i]-(i!=root)}\]
然后考虑换根法,从根x换到根y,设\(g[x]\)表示x作为整个树的根的到达所有叶子节点的数学期望,显然有
\[g[y]=\frac{\frac{(g[x]\times deg[x]-(f[y]+w[x][y]))}{deg[x]-1}+w[x][y]+f[y]\times (deg[y]-1)}{deg[y]}\]
然后我们就可以在\(O(n)\)的时间内解出树的答案,而且还不要特判叶子节点。
对于环的分数,显然是一个环上面吊着很多树,考虑如何从一个点i出发求出数学期望,记为\(h_i\),假设从i出发,现在到达点j,到j的概率为\(gl\),我们有
\(h_i+=\sum\frac{gl}{deg[j]}\times w[i][k]\)(其中k为与j相连的点)
其实原理就是期望\(E=\sum p_ix_i\),其中\(x_i\)可以被拆分成多条边,而拆分出来对于一条边就是到达这条边的概率乘以这条边的长度,于是期望可以单独对于一个部分考虑,只要该部分的长度乘以概率即可。
但是这样显然是\(O(n^2)\),考虑优化,分为环和树处理。
对于树我们先维护\(f_i\),表示以i为根节点的子树中,从i的儿子出发,到达叶子节点的数学期望之和,显然这个前面已经讲述了维护方式,对于环而言,我们维护一个\(g_i\)表示从环上第i个点出发,其中树中的一条边不走,走简单路径走到死的路径长度的数学期望,特别地其中数学期望在树上的部分还没有进行计算。
先考虑求\(g_i\)的顺时针方向的部分,逆时针方向一样,两者加起来就是答案,不妨已经暴力求出了\(g_i\),现在考虑求\(g_{i+1}\),设i进入i-1子树的概率为\(gl\),进行以下操作就可以求出\(g_{i+1}\)
\[g_{i+1}=(g_i-gl\times f[i-1])\times (deg[i]-1)-w[i][i+1]-\frac{f[i+1]}{deg[i+1]-1}+\]
\[\frac{gl\times (deg[i-1]-2)(deg[i]-1)}{deg[i-1]-1}\times (f[i-1]+w[i-1][i])+\frac{gl\times (deg[i-1]-2)(deg[i]-1)}{(deg[i-1]-1)(deg[i]-2)}\times f[i]\]
然后我们就求出了f,g,考虑如何计算最终的答案,枚举环上的每个点,然后下树,记录该点的父亲到根节点的概率\(gl\),特别地,当点i为根节点时\(gl=1\),然后顺便换根求出以该点为根的儿子到整棵树的数学期望之和,记作\(hl\),答案累加\(\frac{hl}{deg[i]}+\frac{gl}{deg[i]}\times g[i]\),特别地当这个点就为根节点的时候应该累加\(\frac{hl+g[i]\times (deg[i]-1)}{deg[i]}\),然后我们就可以\(O(n)\)解决这道题目(常数奇大无比)。
参考代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define il inline #define ri register #define ll long long #define Size 1005000 const int yyb(1000000007); using namespace std; struct point{ point*next;int to,w; }*head[Size]; int lsy,n,m,deg[Size],iv[Size], f[Size],g[Size],vis[Size], hu[Size],hw[Size],w[Size], fa[Size],ht,ans; il int mod(int); il void read(int&),init(), link(int,int,int),work1(),work2(), get_g(); void dfs(int,int),dfs1(int,int),dfs2(int), dfs3(int),dfs4(int,int,int); int main(){init(); if(m==n-1)work1(); else if(m==n)work2(); return 0; } void dfs4(int x,int top,int gl){ if(x==top)f[x]=0;vis[x]=1; for(point*i(head[x]);i!=NULL;i=i->next){ if(vis[i->to])continue; f[x]=(f[x]+(ll)f[i->to]*iv[deg[i->to]-1]+i->w)%yyb; }if(x==top)ans=(ans+(f[x]+(ll)g[x]*(deg[x]-1)%yyb)*iv[deg[x]])%yyb; else ans=(ans+(ll)gl*iv[deg[x]]%yyb*g[top]+(ll)iv[deg[x]]*f[x])%yyb; for(point*i(head[x]);i!=NULL;i=i->next){ if(vis[i->to])continue; f[i->to]=((ll)(f[x]-((ll)f[i->to]*iv[deg[i->to]-1]+ i->w))%yyb*iv[deg[x]-1]+i->w)%yyb; dfs4(i->to,top,(ll)gl*((x^top)?iv[deg[x]-1]:1)%yyb); } } void dfs3(int x){vis[x]=1; for(point*i(head[x]);i!=NULL;i=i->next){ if(vis[i->to])continue;dfs3(i->to); f[x]=(f[x]+(ll)f[i->to]* iv[deg[i->to]-1]%yyb+i->w)%yyb; } } il void get_g(){ int gl(1),cnt(0); for(int i(1);i<=ht;++i) cnt=(cnt+(ll)gl*iv[deg[hu[i]]-1-(i==ht)]%yyb *(f[hu[i]]*(i!=1)+hw[i]*(i!=ht)))%yyb, gl=(ll)gl*iv[deg[hu[i]]-1]%yyb; g[hu[1]]=(g[hu[1]]+cnt)%yyb; for(int i(2),la;i<=ht;++i){ la=i-2;if(!la)la+=ht; gl=(ll)gl*(deg[hu[i-1]]-1)%yyb,cnt=(ll)cnt*(deg[hu[i-1]]-1)%yyb; cnt=(cnt-hw[i-1]-(ll)iv[deg[hu[i]]-1]*f[hu[i]])%yyb; cnt=(cnt-(ll)gl*(deg[hu[la]]-1)%yyb*iv[deg[hu[la]]-2]%yyb*f[hu[la]])%yyb; cnt=(cnt+(ll)gl*(f[hu[la]]+hw[la]))%yyb; cnt=(cnt+(ll)gl*iv[deg[hu[i-1]]-2]%yyb*f[hu[i-1]])%yyb; g[hu[i]]=(g[hu[i]]+cnt)%yyb,gl=(ll)gl*iv[deg[hu[i-1]]-1]%yyb; } } void dfs2(int x){ vis[x]=1; for(point*i(head[x]);i!=NULL;i=i->next){ if(fa[x]==i->to)continue; if(vis[i->to]){int tp(x); while(tp!=i->to) hu[++ht]=tp,hw[ht]=w[tp], tp=fa[tp]; hu[++ht]=i->to,hw[ht]=i->w; fa[i->to]=x;continue; }fa[i->to]=x,w[i->to]=i->w,dfs2(i->to); } } il void work2(){ dfs2(1),memset(vis,0,sizeof(vis)); for(int i(1);i<=ht;++i)vis[hu[i]]=1; for(int i(1);i<=ht;++i)dfs3(hu[i]); get_g(),reverse(hu+1,hu+ht+1); reverse(hw+1,hw+ht),get_g(); memset(vis,0,sizeof(vis)); for(int i(1);i<=ht;++i)vis[hu[i]]=1; for(int i(1);i<=ht;++i)dfs4(hu[i],hu[i],1); printf("%d",mod((ll)ans*iv[n]%yyb)); } il int mod(int x){ return (x%yyb+yyb)%yyb; } void dfs1(int x,int pa){ for(point*i(head[x]);i!=NULL;i=i->next){ if(i->to==pa)continue; g[i->to]=(((ll)g[x]*deg[x]-(ll)(f[i->to]+i->w))%yyb *iv[deg[x]-1]+i->w+(ll)f[i->to]*(deg[i->to]-1) %yyb)%yyb*iv[deg[i->to]]%yyb, dfs1(i->to,x); } } void dfs(int x,int pa){ for(point*i(head[x]);i!=NULL;i=i->next){ if(i->to==pa)continue;dfs(i->to,x); f[x]=(f[x]+f[i->to]+i->w)%yyb; }f[x]=(ll)f[x]*iv[deg[x]-(pa>0)]%yyb; } il void work1(){ dfs(1,0),g[1]=f[1],dfs1(1,0);int ans(0); for(int i(1);i<=n;++i)ans=(ans+g[i])%yyb; printf("%d",mod((ll)ans*iv[n]%yyb)); } il void link(int u,int v,int w){ head[u]=new point{head[u],v,w}; } il void init(){ read(lsy),read(n),read(m); for(int i(1),u,v,w;i<=m;++i) read(u),read(v),read(w), link(u,v,w),link(v,u,w), ++deg[u],++deg[v]; iv[1]=1;for(int i(2);i<=n;++i) iv[i]=-(ll)iv[yyb%i]*(yyb/i)%yyb; } il void read(int &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
t3
给出一个长度为n排列\(\{a_i\}\),给出一个长度为n01序列\(\{b_i\}\),\(b_i\)为1表示应该保留\(a_i\),反之,确定一个区间长度x,每次操作可以选择任意一个区间,删去其中最小的数字,然后右边的数字补齐,询问最大的x保证最后排列变为要保留的数字,而删去所有不需要保留的数字,\(n\leq 10^7\)。
解
显然不能暴力模拟,考虑从小到大选择要删去的\(a_i\),显然最优解中一定存在小的先被删去,然后再删去次小。
我们不妨维护一个\(l_i,r_i\),表示i这个要删去的数字,向左延伸的第一个位置不被删去而比\(a_i\)小,\(r_i\)表示向右延伸到第一个位置不被删去,但是比\(a_i\)小,然后顺便记录\((l_i,r_i)\)这个开区间当中所有的比\(a_i\)大的数字,记为\(bl\),然后对于每一个要删去的位置的\(bl\)取min就是最终答案,这样我们就得到了\(O(n^2)\)暴力,考虑如何优化。
参考代码:
#include <iostream> #include <cstdio> #define il inline #define ri register #define Size 10050 #define intmax 0x7fffffff using namespace std; int a[Size];bool b[Size]; template<class free> il void read(free&); int main(){ int n,ans(intmax);read(n); for(int i(1);i<=n;++i)read(a[i]); for(int i(1);i<=n;++i)read(b[i]); b[0]=b[n+1]=1; for(int i(1),l,r,t;i<=n;++i){ if(b[i])continue;l=r=i,t=1; while(!(b[l]&&a[l]<a[i]))t+=b[l]||a[l]>a[i],--l; while(!(b[r]&&a[r]<a[i]))t+=b[r]||a[r]>a[i],++r; ans=min(ans,t); }printf("%d",ans); return 0; } template<class free> il void read(free &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
显然我们需要快速求出\(l_i,r_i\)以及中间的数字个数,这是一个单调性的问题,我们考虑用单调数据结构维护,不妨从小往大考虑数字,如果当前数字需要保留,就将位置加入平衡树,否则,在平衡树查找比该数字位置恰好小和大的两个位置,这就是我们需要求的\(l_i,r_i\),特别的如果没有,就代表是边界,不妨刚开始就在平衡树中加入位置\(0,n+1\),而正确性在于平衡树现在拥有所有比该数字小的数字。
现在考虑如何查询\((l_i,r_i)\)中满足条件的数字,而直接求不好求,考虑逆向思维,求出不需要的数字,也就是要删去比它小的数字,而注意到我们已经是从小到大考虑数字,所以前面的数字必然都要比该个数字小,于是我们只需要维护位置即可,每次了来一个要被删去的数字,在其对应的位置上+1,然后查\((l,r)\)就可以得到,这样就得到一个常数很大的\(nlog(n)\)。
参考代码
#include <iostream> #include <cstdio> #include <algorithm> #include <set> #define il inline #define ri register #define Size 1000500 #define intmax 0x7fffffff using namespace std; struct data{ int a,c;bool b; }d[Size]; set<int>S; int a[Size],n,ans(intmax); set<int>::iterator l,m,r; il int ask(int); template<class free> il void read(free&); il void change(int,int); il bool comp(const data&,const data&); int main(){ read(n),++n; for(int i(2);i<=n;++i)read(d[i].a),d[i].c=i; for(int i(2);i<=n;++i)read(d[i].b);++n; d[1]={0,1,1},d[n]={0,n,1},sort(d+1,d+n+1,comp); for(int i(1);i<=n;++i) if(d[i].b)S.insert(d[i].c); else{ m=S.insert(d[i].c).first,l=r=m,--l,++r; ans=min(ans,*r-*l-1-(ask(*r)-ask(*l-1))), change(*m,1),S.erase(m); }printf("%d",ans); return 0; } il int ask(int p){ int ans(0);while(p)ans+=a[p],p-=-p&p;return ans; } il void change(int p,int x){ while(p<=n)a[p]+=x,p+=-p&p; } il bool comp(const data&a,const data&b){ return a.a<b.a; } template<class free> il void read(free &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
考虑进一步优化,发现一个性质,就是对于一个要被删去的数字i的\((l_i,r_i)\)而言,如果这个开区间范围内存在一个要被删除的数字j比i大,那么\((l_j,r_j)\)必然被\((l_i,r_i)\)包含,于是j必然成为最优解,不断递归下去最终得到一个k,\((l_k,r_k)\)中不包含一个要被删除的数字比它大,于是我们只要考虑求出每个要被删除的数字的\((l_i,r_i)\)中不要被删除的比它大的数字,这个显然维护出前缀和\(s_i\)表示位置i前面不要被删除的数字,查询\(s_{r_i-1}-s_{l_i}\),关键在求\(l_i,r_i\),不妨一次求一个,先考虑求\(l_i\),从左往右扫描,考虑数字i,现在关键是找到最近的一个位置保证该数字不被删除,而且要小于i,考虑离散化维护树状数组,维护区间最大值,每次来一个不要被删除的位置,直接在该个数值对应的位置,将其位置加入,于是查询,只要查询,\(1\sim i\)的最大值就可以得到答案,同理\(r_i\)也是一个方法,于是我们得到一个常数很小的\(O(nlog(n))\)。
参考代码:
#include <iostream> #include <cstdio> #include <cstring> #define il inline #define ri register #define Size 1005000 #define intmax 0x7fffffff using namespace std; bool b[Size],flag; int a[Size],c[Size],n,l[Size],s[Size]; template<class free> il void read(free&);il int ask(int); il void change(int,int),cmp(int&,int); int main(){ read(n);int ans(intmax); for(int i(1);i<=n;++i)read(a[i]); for(int i(1);i<=n;++i)read(b[i]); for(int i(1);i<=n;++i){ s[i]=s[i-1]+b[i]; if(b[i])change(a[i],i); else l[i]=ask(a[i]); }for(int i(1);i<=n;++i)c[i]=n+1; flag=1;for(int i(n);i;--i){if(b[i])change(a[i],i); else ans=min(ans,s[ask(a[i])-1]-s[l[i]]+1); }printf("%d",ans); return 0; } il int ask(int p){ int ans((flag?n+1:0)); while(p)cmp(ans,c[p]),p-=-p&p; return ans; } il void cmp(int &p,int x){ if(flag)p=p<x?p:x; else p=p>x?p:x; } il void change(int p,int x){ while(p<=n)cmp(c[p],x),p+=-p&p; } template<class free> il void read(free &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }
考虑进一步优化,显然题目需要\(O(n)\),考虑是否算重,对于已经被i访问过的端点,如果j来访问,发现还可以延伸,那么必然j小于端点对应的数字,而\((l_i,r_i)\)间的可以被计算的数字必然比端点大,于是全部可以成为j的答案,于是i必然比j优秀。
显然一个数字所访问的位置需要被标记,如果直接从左往右扫描,然后延伸,会存在有数字直接在被标记的位置上,这样优化效果就不明显,更换扫描顺序,从大的数字枚举到小的数字,这样如果有数字出现在被标记的位置,且结果还是会更加优秀,因此只要从大往小扫描,暴力扩展并且标记,如果遇到标记,就考虑下一个数字,就可以做到\(O(n)\)。
参考代码:
#include <iostream> #include <cstdio> #define il inline #define ri register #define Size 10000050 #define intmax 0x7fffffff using namespace std; int a[Size],b[Size],c[Size],d[Size]; il void read(int&); int main(){ int n,ans(intmax);read(n); for(ri int i(1);i<=n;++i)read(a[i]); for(ri int i(1);i<=n;++i) read(b[i]),c[a[i]]=i;b[0]=b[n+1]=1; for(ri int i(n),l,r,s;i;--i){ if(b[c[i]])continue;l=r=c[i],--l,++r,s^=s; while(!(b[l]&&a[l]<a[c[i]])){if(d[l])goto end;s+=b[l],d[l]=i,--l;} while(!(b[r]&&a[r]<a[c[i]])){if(d[r])goto end;s+=b[r],d[r]=i,++r;} ans=min(ans,s+1);end:; }printf("%d",ans); return 0; } il void read(int &x){ x^=x;ri char c;while(c=getchar(),c<'0'||c>'9'); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar(); }