好順位で全完できたのでよかった。
https://www.hackerrank.com/contests/101hack49/challenges/bigger-arrays
問題
同じ長さの数列A,Bに対しAがBより大きいとは、各要素iについてA[i]>B[i]を満たしていることをいう。
数列Sに対し、1≦X[i]≦S[i]で定義されるすべての数列Xを考える。
F(S)とは、そのようなXのうち、他のいずれの数列もXより大きいものがないようなものの数を意味する。
数列Aに対し、以下のクエリに適宜答えよ。
- 入力(L,R,X)に対し、Aの部分列A[L...R]をすべてXに置き換える
- 入力(L,R)に対し、Aの部分列B=A[L...R]に対するF(B)を答える
解法
F(S)に含まれる条件は、X[i]=S[i]となるiが1つ以上含まれることである。
逆にすべてのiに対しX[i]<S[i]の場合、XはF(S)にカウントされない。
よってF(S)=prod(S[i])-prod(S[i]-1)である。
そこで、部分列の積を計算する遅延伝搬SegTreeを用い、A[i]の積と(A[i]-1)の積を管理していけば終わり。
ll mo=1000000007; ll modpow(ll a, ll n = mo-2) { ll r=1; while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1; return r; } template<int NV> class SegTree_Lazy { public: vector<ll> val,mul; SegTree_Lazy(){val.resize(NV*2,1); mul.resize(NV*2,1);}; ll comp(ll l,int r){ return l*r%mo;}; ll getval(int x,int y,int l=0,int r=NV,int k=1) { if(r<=x || y<=l) return 1; if(x<=l && r<=y) return mul[k]; x=max(x,l); y=min(y,r); if(val[k]>=0) { return modpow(val[k],y-x); } return comp(getval(x,y,l,(l+r)/2,k*2),getval(x,y,(l+r)/2,r,k*2+1)); } void update(int x,int y,int v,int l=0,int r=NV,int k=1) { if(l>=r) return; if(x<=l && r<=y) { val[k]=v; mul[k]=modpow(v,r-l); } else if(l < y && x < r) { if(val[k]!=-1) { val[k*2]=val[k*2+1]=val[k]; mul[k*2]=mul[k*2+1]=modpow(val[k],(r-l)/2); val[k]=-1; } update(x,y,v,l,(l+r)/2,k*2); update(x,y,v,(l+r)/2,r,k*2+1); mul[k]=comp(mul[k*2],mul[k*2+1]); } } }; int N,Q; SegTree_Lazy<1<<20> st1,st2; void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>Q; FOR(i,N) { cin>>x; st1.update(i,i+1,x); st2.update(i,i+1,x-1); } while(Q--) { cin>>i>>x>>y; if(i==1) { cin>>j; st1.update(x-1,y,j); st2.update(x-1,y,j-1); } else { ll a=st1.getval(x-1,y)-st2.getval(x-1,y); cout<<(a+mo)%mo<<endl; } } }
まとめ
これは方針はすぐ立ったけど、遅延伝搬SegTreeを書くのに手間取って遅れた。