文章目录
- 逆序对
- 对序列
- 未曾设想的道路
牛客IOI周赛26-提高组
逆序对
这种套路之前已经见过几次了,肯定不是模拟操作数列
-
opt 1
对于i∈[1,l)⋃(r,n]i∈[1,l)\bigcup(r,n]i∈[1,l)⋃(r,n] 逆序对是不影响的
对于i∈(l,r)i∈(l,r)i∈(l,r) 与l/rl/rl/r的情况会反转,之前是逆序对以后便不是,反之同理
逆序对的改变情况为222(偶数)所以奇偶不变
因此整个序列的逆序对奇偶因为l,rl,rl,r的互换一定发生改变
(
ps:
l≠rl≠rl=r) -
opt 2
反转区间[l,r][l,r][l,r]
对于i∈[1,l)⋃(r,n]i∈[1,l)\bigcup (r,n]i∈[1,l)⋃(r,n] 同样不影响
操作区间的总配对个数cnt=len×(len−1)2cnt=\frac{len\times (len-1)}{2}cnt=2len×(len−1),假设原区间逆序对个数为xxx,则反转后为cnt−xcnt-xcnt−x
发现最后的奇偶取决于cntcntcnt的奇(奇加偶)偶(奇加奇/偶加偶)
-
opt 3/4
左移右移本质上是一样的,以左移为例
将整个操作区间重新离散化
对于区间的开头假设离散化结果为xxx,意味着原来该开头的逆序对贡献为x−1x-1x−1
左移到最后,就会与前面的n−xn-xn−x个数产生逆序对
一次移动改变数为x−1+n−x=−2x+n−1x-1+n-x=-2x+n-1x−1+n−x=−2x+n−1
发现xxx的贡献因为带了−2-2−2一定是偶数,不改变奇偶,无用直接不管
所以最后只取决于k×(n−1)k\times (n-1)k×(n−1)的奇偶性
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 200005
int n, m;
int a[maxn], tree[maxn], tmp[maxn];int lowbit( int x ) {return x & ( -x );
}void Add( int x ) {for( int i = x;i <= n;i += lowbit( i ) )tree[i] ++;
}int Ask( int x ) {int ans = 0;for( int i = x;i;i -= lowbit( i ) )ans += tree[i];return ans;
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%d", &a[i] );int ans = 0;for( int i = n;i;i -- )ans += Ask( a[i] ), Add( a[i] );ans = ans & 1;while( m -- ) {int opt, l, r, k;scanf( "%d %d %d", &opt, &l, &r );switch( opt ) {case 1 : {if( l != r ) ans ^= 1;break;}case 2 : {int len = ( r - l + 1 ) * ( r - l ) / 2;if( len & 1 ) ans ^= 1;break;}case 3 : {scanf( "%d", &k );int len = r - l + 1;if( ( k * ( len + 1 ) ) & 1 ) ans ^= 1;break;}case 4 : {scanf( "%d", &k );int len = r - l + 1;if( ( k * ( len - 1 ) ) & 1 ) ans ^= 1;break;}}printf( "%d\n", ans );}return 0;
}
对序列
序列合法的必要条件为:对于xxx,第二次出现位置后面所有值都不能比xxx小
考虑反过来,剩下nnn个格子,最大能填mmm
fn,m=fn,m−1+∑i=1nfn−i,m−1×(n−i+1)f_{n,m}=f_{n,m-1}+\sum_{i=1}^nf_{n-i,m-1}\times (n-i+1)fn,m=fn,m−1+∑i=1nfn−i,m−1×(n−i+1)
fn,m−1:f_{n,m-1}:fn,m−1: 不填mmm的方案数
fn,m=fn,m−1+∑i=1nfn−i,m−1×(n−i+1)f_{n,m}=f_{n,m-1}+\sum_{i=1}^nf_{n-i,m-1}\times (n-i+1)fn,m=fn,m−1+∑i=1nfn−i,m−1×(n−i+1)
⇔fn,m=fn,m−1+∑i=0n−1fi,m−1×(i+1)\Leftrightarrow f_{n,m}=f_{n,m-1}+\sum_{i=0}^{n-1}f_{i,m-1}\times (i+1)⇔fn,m=fn,m−1+∑i=0n−1fi,m−1×(i+1)
gn,m=∑i=0n−1fi,m×(i+1)⇒gn,m=gn−1,m+fn−1,m×ng_{n,m}=\sum_{i=0}^{n-1}f_{i,m}\times(i+1)\Rightarrow g_{n,m}=g_{n-1,m}+f_{n-1,m}\times ngn,m=∑i=0n−1fi,m×(i+1)⇒gn,m=gn−1,m+fn−1,m×n
前缀和优化
#include <cstdio>
#define mod 998244353
#define int long long
#define maxn 1005
int f[maxn], g[maxn];
int n, m;signed main() {scanf( "%lld %lld", &n, &m );f[0] = 1;for( int i = 1;i <= n;i ++ )g[i] = ( g[i - 1] + f[i - 1] * i % mod ) % mod;for( int j = 1;j <= m;j ++ )for( int i = 1;i <= n;i ++ ) {f[i] = ( f[i] + g[i] ) % mod;g[i] = ( g[i - 1] + f[i - 1] * i % mod ) % mod;}printf( "%lld\n", f[n] );return 0;
}
未曾设想的道路
维护连续的懒标记,前kkk大的懒标记,现在前kkk大,历史前kkk大
无脑树套树
代码是正确的,卡卡常就能过了
#pragma GCC optimize(2)
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define inf -1e9
#define maxn 100005
#define maxk 101
#define lson num << 1
#define rson num << 1 | 1
#define Pair pair < int, int >
struct node {int lazy;int tag[maxk], maxx[maxk], now[maxk];node() {for( int i = 1;i < maxk;i ++ )tag[i] = maxx[i] = now[i] = inf;}
}t[maxn << 2];
priority_queue < Pair > q;
int n, m;
int h[maxn], tmp[maxk], ans[maxk];
//tag:[1,k]max lazy till now
//maxx:[1,k]max Height up till now
//now:[1,k]max Height at the present void merge( int *A, int *B ) {static int MS[maxk];int i = 1, j = 1;for( int k = 1;k < maxk;k ++ )if( A[i] > B[j] ) MS[k] = A[i ++];else MS[k] = B[j ++];memcpy( A, MS, sizeof( MS ) );
}void pushup( int num ) {memcpy( t[num].now, t[lson].now, sizeof( t[lson].now ) );merge( t[num].now, t[rson].now );memcpy( t[num].maxx, t[lson].maxx, sizeof( t[lson].maxx ) );merge( t[num].maxx, t[rson].maxx );
}void build( int num, int l, int r ) {if( l == r ) {t[num].maxx[1] = t[num].now[1] = h[l];return; }int mid = ( l + r ) >> 1;build( lson, l, mid );build( rson, mid + 1, r );pushup( num );
}void unite( int *A, int *B ) {static int MS[maxk];while( ! q.empty() ) q.pop();for( int i = 1;i < maxk;i ++ )q.push( make_pair( A[i] + B[1], i ) ), MS[i] = 1;for( int i = 1;i < maxk;i ++ ) {Pair now = q.top(); q.pop();tmp[i] = max( (int)inf, now.first );q.push( make_pair( A[now.second] + B[++ MS[now.second]], now.second ) );}
}void pushdown( int num ) {if( t[num].tag[1] > -1e8 ) {for( int i = 1;i < maxk;i ++ )tmp[i] = t[lson].lazy + t[num].tag[i];merge( t[lson].tag, tmp );for( int i = 1;i < maxk;i ++ )tmp[i] = t[rson].lazy + t[num].tag[i];merge( t[rson].tag, tmp );t[lson].lazy += t[num].lazy;t[rson].lazy += t[num].lazy;unite( t[num].tag, t[lson].now );merge( t[lson].maxx, tmp );unite( t[num].tag, t[rson].now );merge( t[rson].maxx, tmp );for( int i = 1;i < maxk;i ++ ) {t[lson].now[i] += t[num].lazy;t[rson].now[i] += t[num].lazy;}for( int i = 1;i < maxk;i ++ )t[num].tag[i] = inf;t[num].lazy = 0;}
}void modify( int num, int l, int r, int L, int R, int x ) {if( r < L || R < l ) return;if( L <= l && r <= R ) {t[num].lazy += x;for( int i = 1;i < maxk;i ++ ) tmp[i] = inf; tmp[1] = t[num].lazy;for( int i = 1;i < maxk;i ++ ) t[num].now[i] += x;merge( t[num].tag, tmp );merge( t[num].maxx, t[num].now );return;}pushdown( num );int mid = ( l + r ) >> 1;modify( lson, l, mid, L, R, x );modify( rson, mid + 1, r, L, R, x );pushup( num );
}void query( int num, int l, int r, int L, int R ) {if( R < l || r < L ) return;if( L <= l && r <= R ) {merge( ans, t[num].maxx );return;}int mid = ( l + r ) >> 1;pushdown( num );query( lson, l, mid, L, R );query( rson, mid + 1, r, L, R );
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%d", &h[i] );build( 1, 1, n );int opt, l, r, k;while( m -- ) {scanf( "%d %d %d %d", &opt, &l, &r, &k );if( ! opt ) modify( 1, 1, n, l, r, k );else {for( int i = 1;i < maxk;i ++ ) ans[i] = inf;query( 1, 1, n, l, r );int ret;for( int i = 1;i <= k;i ++ )if( ans[i] > -1e8 ) ret = ans[i];printf( "%d\n", ret );}}return 0;
}