Solution -「多校聯訓」古老的序列問題
阿新 • • 發佈:2021-10-04
\(\mathcal{Description}\)
Link.
給定序列 \(\{a_n\}\),和 \(q\) 次形如 \([L,R]\) 的詢問,每次回答
\[\sum_{[l,r]\subseteq [L,R]}\min_{i=l}^r\{a_i\}\cdot\max_{i=l}^r\{a_i\}\pmod{10^9+7}. \]\(n,q\le10^5\)。
\(\mathcal{Solution}\)
瞬間聯想到 這道題,嘗試把詢問掛到貓樹上分治處理。對於分治區間 \([l,r]\),令其中點為 \(p\),考慮離線處理掛在它身上的詢問。
而此時,我們需要用掃描線進行進一步轉化:左端點 \(i\)
- 最小值、最大值在 \([i,p]\);
- 最小值在 \([i,p]\),最大值在 \((p,r]\);
- 最大值在 \([i,p]\),最小值在 \((p,r]\);
- 最小值、最大值在 \((p,r]\)。
簡直和上面那題一模一樣呢。發現貢獻無非是左邊的最小/最大值等形式的係數乘上右邊相同形式的係數,詢問時即求右邊貢獻的字首和。可以用四科線段樹維護四種類型的貢獻。複雜度 \(\mathcal O((q+n\log n)\log n)\)
Ummm... 有 \(\mathcal O(n\log n)\) 的做法,大概是隻用掃描線,然後矩陣線段樹維護歷史和,見 祂的部落格。
\(\mathcal{Code}\)
/*~Rainybunny~*/ #include <bits/stdc++.h> #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i ) #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i ) inline char fgc() { static char buf[1 << 17], *p = buf, *q = buf; return p == q && ( q = buf + fread( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p++; } inline int rint() { int x = 0, s = fgc(); for ( ; s < '0' || '9' < s; s = fgc() ); for ( ; '0' <= s && s <= '9'; s = fgc() ) x = x * 10 + ( s ^ '0' ); return x; } inline void wint( const int x ) { if ( 9 < x ) wint( x / 10 ); putchar( x % 10 ^ '0' ); } const int MAXN = 1e5, MOD = 1e9 + 7; int n, q, a[MAXN + 5], ans[MAXN + 5]; inline int imin( const int u, const int v ) { return u < v ? u : v; } inline int imax( const int u, const int v ) { return u < v ? v : u; } inline int mul( const int u, const int v ) { return 1ll * u * v % MOD; } inline int add( int u, const int v ) { return ( u += v ) < MOD ? u : u - MOD; } inline void addeq( int& u, const int v ) { ( u += v ) >= MOD && ( u -= MOD ); } struct Atom { int l, r, id; inline bool operator < ( const Atom& t ) const { return l < t.l; } }; std::vector<Atom> qbuc[MAXN << 2]; int all[MAXN << 2]; inline void hang( const int u, const int l, const int r, const int ql, const int qr, const int qid ) { if ( ql <= l && r <= qr ) return qbuc[u].push_back( { l, r, qid } ); int mid = l + r >> 1; if ( qr <= mid ) hang( u << 1, l, mid, ql, qr, qid ); else if ( mid < ql ) hang( u << 1 | 1, mid + 1, r, ql, qr, qid ); else { qbuc[u].push_back( { ql, qr, qid } ); hang( u << 1, l, mid, ql, mid, qid ); hang( u << 1 | 1, mid + 1, r, mid + 1, qr, qid ); } } struct SegmentTree { // how to replace it with BIT? int sum[MAXN << 2], coe[MAXN << 2], tag[MAXN << 2]; inline void pushad( const int u, const int v ) { addeq( tag[u], v ), addeq( sum[u], mul( coe[u], v ) ); } inline void pushdn( const int u ) { if ( tag[u] ) { pushad( u << 1, tag[u] ), pushad( u << 1 | 1, tag[u] ); tag[u] = 0; } } inline void pushup( const int u ) { sum[u] = add( sum[u << 1], sum[u << 1 | 1] ); } inline void build( const int u, const int l, const int r, const int* c ) { sum[u] = tag[u] = 0; if ( l == r ) return void( coe[u] = c == NULL ? 1 : c[l] ); int mid = l + r >> 1; build( u << 1, l, mid, c ), build( u << 1 | 1, mid + 1, r, c ); coe[u] = add( coe[u << 1], coe[u << 1 | 1] ); } inline void modify( const int u, const int l, const int r, const int ml, const int mr, const int v ) { // if ( l > r ) return ; // well, I'll promise it. if ( ml <= l && r <= mr ) return pushad( u, v ); int mid = l + r >> 1; pushdn( u ); if ( ml <= mid ) modify( u << 1, l, mid, ml, mr, v ); if ( mid < mr ) modify( u << 1 | 1, mid + 1, r, ml, mr, v ); pushup( u ); } inline int query( const int u, const int l, const int r, const int ql, const int qr ) { if ( ql <= l && r <= qr ) return sum[u]; int mid = l + r >> 1, ret = 0; pushdn( u ); if ( ql <= mid ) addeq( ret, query( u << 1, l, mid, ql, qr ) ); if ( mid < qr ) addeq( ret, query( u << 1 | 1, mid + 1, r, ql, qr ) ); return ret; } } sgt[4]; inline void solve( const int u, const int l, const int r ) { /* bound check and divide down. */ auto& qry( qbuc[u] ); if ( l == r ) { all[u] = mul( a[l], a[l] ); for ( const auto& x: qry ) addeq( ans[x.id], all[u] ); return ; } int mid = l + r >> 1; solve( u << 1, l, mid ), solve( u << 1 | 1, mid + 1, r ); all[u] = add( all[u << 1], all[u << 1 | 1] ); /* initialize some information. */ static int x[MAXN + 5], y[MAXN + 5], xy[MAXN + 5]; // x->min, y->max. x[mid] = y[mid] = a[mid]; per ( i, mid - 1, l ) { x[i] = imin( a[i], x[i + 1] ), y[i] = imax( a[i], y[i + 1] ); } x[mid + 1] = y[mid + 1] = a[mid + 1]; rep ( i, mid + 2, r ) { x[i] = imin( a[i], x[i - 1] ), y[i] = imax( a[i], y[i - 1] ); } rep ( i, l, r ) xy[i] = mul( x[i], y[i] ); #define R 1, 1, r - mid sgt[0].build( R, NULL ); sgt[1].build( R, x + mid ); sgt[2].build( R, y + mid ); sgt[3].build( R, xy + mid ); /* finally begin to solve queries. */ std::sort( qry.begin(), qry.end() ); for ( int i = mid, j = int( qry.size() ) - 1, px = mid + 1, py = mid + 1; i >= l; --i ) { while ( px <= r && x[i] <= x[px] ) ++px; while ( py <= r && y[i] >= y[py] ) ++py; int pl = imin( px, py ), pr = imax( px, py ); if ( mid + 1 < pl ) sgt[0].modify( R, 1, pl - mid - 1, xy[i] ); if ( px < py ) sgt[1].modify( R, px - mid, py - mid - 1, y[i] ); if ( py < px ) sgt[2].modify( R, py - mid, px - mid - 1, x[i] ); if ( pr <= r ) sgt[3].modify( R, pr - mid, r - mid, 1 ); while ( ~j && qry[j].l == i ) { int qr = qry[j].r - mid; addeq( ans[qry[j].id], add( add( sgt[0].query( R, 1, qr ), sgt[1].query( R, 1, qr ) ), add( sgt[2].query( R, 1, qr ), sgt[3].query( R, 1, qr ) ) ) ); if ( i == l && qry[j].r == r ) addeq( ans[qry[j].id], all[u] ); --j; } } /* update all[u] with contribution in current section. */ addeq( all[u], add( add( sgt[0].query( R, 1, r - mid ), sgt[1].query( R, 1, r - mid ) ), add( sgt[2].query( R, 1, r - mid ), sgt[3].query( R, 1, r - mid ) ) ) ); #undef R } int main() { freopen( "sequence.in", "r", stdin ); freopen( "sequence.out", "w", stdout ); n = rint(), q = rint(); rep ( i, 1, n ) a[i] = rint(); rep ( i, 1, q ) { int l = rint(), r = rint(); hang( 1, 1, n, l, r, i ); } solve( 1, 1, n ); rep ( i, 1, q ) wint( ans[i] ), putchar( '\n' ); return 0; }