Loading

模拟50—「第零题·第负一题·第负二题」

第零题

这道题考场上做的很曲折,并且最终没有做出来。
一开始几个思路都是假的,但是我没看出来。

到后面已经明确倍增 \(k\) 的思路了,但是还是在用树上主席树+前缀和,导致没有正确性。

正解关键的一步是 先用倍增求出来每个点跳到第一个死的地方是哪里,记为 \(nxt_i\)
然后对 \(nxt_i\) 倍增。

从下到上很简单,如果要从上到下的话,还有些问题,因为直接莽走上下可能并不等价。

需要一个操作就是先让他从上到下的路程上剩余的血量消耗光,然后在从上到下倍增到被消耗光的那个点。
这样是上下就是等价的,很显然。

这题不算很难,但是思考时间不够,总是认为自己很难实现的思路是对的,然而确实没有什么正确性。
心态还容易被搞炸,多想,思考越多,实现越简单。

第负一题

首先可以发现可以在每个左端点做一个简单的 \(dp\) 拿到部分分数 \(f_i=max(f_{i-1},f_{i-2}+a[i])\)

考场上一直在想怎么快速求出这个 \(dp\) 的前缀和,然鹅并没有什么效果。

他让求的是所有区间的答案,显然我们无法遍历所有区间,所以总要想个方式一下计算很多区间的答案。

帮助我们达到这个效果的就是 分治

考场上完全没有想到这个思路,甚至现在也不能很理解这是怎么想到的,暂且当作一种统计全区间问题的一个套路

分治的思想就是定义 \(solve(l,r)\) 代表计算 \([l,r]\) 内所有区间的答案。

显然可以把所有区间分成两类 ,跨过 \(mid\) 的和没跨过 \(mid\) 的。
对于第二种,直接递归下去计算,现在考虑如何在与 \(O(r-l)\) 相关的复杂度内计算出来跨过 \(mid\) 的答案。

正确的操作方式是从 \(mid\) 向左端点和右端点分别做 \(dp\)
计算出来 \(f_{0/1,i},g_{0/1,i}\)分别代表 选/不选 mid/mid+1 的情况下 \(i\) 处的最大值。

转移很简单,重点是拼接。

有两种方式拼接

\[\begin{cases} f_{i,1} + g_{j,0} \\ f_{i,0} + max( g_{j,0},g_{j,1} ) \end{cases}\]

求出来 \(dp\) 值之后,他们的顺序就不重要了。
列出不等式,解出来选择一个 \(f_{i,0}\) 的范围,然后对 \(g\) 操作一下排个序,就可以二分+前缀和完成操作。

这道题虽然看题解很好写出来,但是对于我还是有几个思维峰点不太想的到。

第负二题

题不错,就是数据造垃圾了。

先转化一下题意,不难发现,如果 \(k\) 可行,那么至少要有一个 大小为 \(k\) 的菱形。

然后推一下柿子在转化一下,可以解出来中心的坐标范围。

就是要让一个 \(max\) 和一个 \(min\) 有交。

可以直接 二分答案加 \(rmq\) 实现 \(nlogn\)

然后就是比较 \(nb\) 的部分了。

根据性质可以发现 \(|f_i-f_{i-1}|\leq1\)
所以我们每次不需要二分答案,而是直接\(check \ \ \ ( f_[i-1]-1,f[i-1],f[i-1]+1)\) 是否合法。

现在考虑如何优化掉 \(rmq\)

发现其实是让一个范围内的区间有交,而我们可以用单调队列来维护这个值。

每个\(i\) 来说,他管辖的区间就是 \([i-f_i+1,i] \ \ \ (j\leq i)\) \([i,i+f_i-1] \ \ \ (j\geq i )\)

由于\(i\)\(f_i\) 的变化范围有限且单调不减,所以可以用单调队列解决。

注意单调队列无法回到之前的状态,所以在确定答案之前不改变单调队列。
而是通过手动增加、减去这些值来实现。

把同类操作写成函数可以大幅减小调试难度,注意判断队列为空的情况。

code
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <algorithm>
#define R register int
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
#define fre(x) freopen( #x".in" , "r" , stdin ) , freopen( #x".out" , "w" , stdout )

int Ruusupuu ;
FILE * rsp_5u ;

using namespace std ;
typedef unsigned long long u64;
typedef long long L ;
typedef double D ;
const int N = 5e6 + 10 ;
const int P = 998244353 ;
const int Inf = 1e9 + 98 ;

inline int read(){
	int w= 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ 48 ) , ch = getchar() ; 
	return fg ? -w : w ;
}

int n , Ll , X , Y , l [N] , r [N] , f [N] , pow [N] , ans ;
int q [5][N] , h [5] , t [5] ;
u64 A , B ;
   
u64 xorshift128p(u64 &A, u64 &B) { 
    u64 T = A, S = B; 
    A = S; 
    T ^= T << 23; 
    T ^= T >> 17; 
    T ^= S ^ (S >> 26); 
    B = T; 
    return T + S; 
} 
   
void gen(int n, int L, int X, int Y, u64 A, u64 B, int l[], int r[]) { 
    for (int i = 1; i <= n; i ++) { 
        l[i] = xorshift128p(A, B) % L + X; 
        r[i] = xorshift128p(A, B) % L + Y; 
        if (l[i] > r[i]) swap(l[i], r[i]); 
    } 
}

void sc(){
	n = read() , Ll = read() , X = read() , Y = read() , scanf( "%llu%llu" , &A , &B ) , gen( n , Ll , X , Y , A , B , l , r ) ;
//	n = read() ; for( R i = 1 ; i <= n ; i ++ ) l [i] = read() , r [i] = read() ;
}

int getval( int ix , int id ){
	if( ix == 1 ) return l [id] + id ;
	if( ix == 2 ) return r [id] - id ;
	if( ix == 3 ) return l [id]	- id ;
	if( ix == 4 ) return r [id] + id ;
	assert( 0 ) ;
}

int askhead( int ix ){
	if( h [ix] <= t [ix] ) return getval( ix , q [ix][h [ix]] ) ;
	else return ( ix & 1 ) ? -Inf : Inf ;
}

void moveval( int ix , int val ){
	if( ix == 1 || ix == 3 ) while( h [ix] <= t [ix] && val >= getval( ix , q [ix][t [ix]] ) ) t [ix] -- ;
	if( ix == 2 || ix == 4 ) while( h [ix] <= t [ix] && val <= getval( ix , q [ix][t [ix]] ) ) t [ix] -- ;
}

void moveid( int ix , int id ){
	while( h [ix] <= t [ix] && q [ix][h [ix]] < id ) h [ix] ++ ;
}

void debug(){
	for( R i = 1 ; i <= 4 ; i ++ , puts( "" ) ){
		printf( "DEBUGING%d %d %d\n" , i , h [i] , t [i] ) ;
		for( R j = h [i] ; j <= t [i] ; j ++ ) printf( "%d " , q [i][j] ) ;
	}
}

void work(){
	f [1] = 1 , q [1][h [1] = t [1] = 1] = 1 ;
	q [2][h [2] = t [2] = 1] = 1 ;
	q [3][h [3] = t [3] = 1] = 1 ;
	q [4][h [4] = t [4] = 1] = 1 ;
	for( R i = 2 ; i <= n ; i ++ ){
		moveval( 1 , getval( 1 , i ) ) , q [1][++ t [1]] = i ;
		moveval( 2 , getval( 2 , i ) ) , q [2][++ t [2]] = i ;
		moveid( 3 , i ) , moveid( 4 , i ) ;
	
		{
			int k = f [i - 1] ;
			if( i < k || i + k > n ) goto ZT ;
			int max1 = askhead( 1 ) + k - i , min2 = askhead( 2 ) + i - k ; 
			int max3 = max( askhead( 3 ) , max( getval( 3 , i + f [i - 1] - 1 ) , getval( 3 , i + f [i - 1] ) ) ) + i + k ;
			int min4 = min( askhead( 4 ) , min( getval( 4 , i + f [i - 1] - 1 ) , getval( 4 , i + f [i - 1] ) ) ) - k - i ;
			if( max( max1 , max3 ) <= min( min2 , min4 ) ){
				f [i] = f [i - 1] + 1 ;
				moveval( 3 , getval( 3 , i + f [i - 1] - 1 ) ) , q [3][++ t [3]] = i + f [i - 1] - 1 ;
				moveval( 3 , getval( 3 , i + f [i - 1] ) ) , q [3][++ t [3]] = i + f [i - 1] ;
				moveval( 4 , getval( 4 , i + f [i - 1] - 1 ) ) , q [4][++ t [4]] = i + f [i - 1] - 1 ;
				moveval( 4 , getval( 4 , i + f [i - 1] ) ) , q [4][++ t [4]] = i + f [i - 1] ;
				continue ;
			}
		}
		
		ZT: ; {
			
			int k = f [i - 1] - 1 ;
			if( i < k || i + k > n ) goto zt ;
			int max1 , min2 ;
			int max3 = max( askhead( 3 ) , getval( 3 , i + f [i - 1] - 1 ) ) + i + k ;
			int min4 = min( askhead( 4 ) , getval( 4 , i + f [i - 1] - 1 ) ) - i - k ;
			if( q [1][h [1]] == i - f [i - 1] ){
				if( h [1] + 1 <= t [1] ) max1 = getval( 1 , q [1][h [1] + 1] ) + k - i ;
				else max1 = -Inf ;
			} else max1 = askhead( 1 ) + k - i ;
			if( q [2][h [2]] == i - f [i - 1] ){
				if( h [2] + 1 <= t [2] ) min2 = getval( 2 , q [2][h [2] + 1] ) - k + i ;
				else min2 = Inf ;
			} else min2 = askhead( 2 ) - k + i ;
			
			if( max( max1 , max3 ) <= min( min2 , min4 ) ){
				f [i] = f [i - 1] ;
				moveid( 1 , i - f [i - 1] + 1 ) , moveid( 2 , i - f [i - 1] + 1 ) ;
				moveval( 3 , getval( 3 , i + f [i - 1] - 1 ) ) , q [3][++ t [3]] = i + f [i - 1] - 1 ;
				moveval( 4 , getval( 4 , i + f [i - 1] - 1 ) ) , q [4][++ t [4]] = i + f [i - 1] - 1 ;
				continue ;
			}
		}
		
		zt: ;{
			f [i] = f [i - 1] - 1 ;
			moveid( 1 , i - f [i] + 1 ) , moveid( 2 , i - f [i] + 1 ) ;
		}
	} pow [0] = 1 ; for( R i = 1 ; i <= n ; i ++ ) pow [i] = ( 1ll * pow [i - 1] * 3 ) % P ;
	for( R i = 1 ; i <= n ; i ++ ) ans = ( 0ll + ans + ( 1ll * pow [i - 1] * f [i] ) ) % P ;
	printf( "%d\n" , ans ) ;
}

signed main(){
//	fre( in ) ;
	sc() ;
	work() ;
	return 0 ;
}

总结

这场爆炸了,主要原因就是T1。
思考一定时间在码,太麻烦一般不对(貌似重复过无数遍)

一定还是先把暴力打了,把题看了,思考是最重要的,否则浪费题目。
T2 貌似很nb,因为完全没想到。

posted @ 2021-09-11 14:44  Soresen  阅读(76)  评论(3)    收藏  举报