大佬
\(link\)
这道题如果写对\(dfs\)XIN队,可以得到\(40pts\)
但是\(dfs\)也不是那么好写的,尤其是边界条件要注意
代码注释应该可以看懂
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 1e5 + 10 ;
int Ruusupuu ;
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 ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , m , k , a [N] , w [N] , c [N] ;
bool fg ;
void sc(){
n = read() , m = read() , k = read() ;
for( R i = 1 ; i <= n ; i ++ ) a [i] = read() ;
for( R i = 1 ; i <= n ; i ++ ) w [i] = read() ;
for( R i = 1 ; i <= m ; i ++ ) c [i] = read() ;
}
inline bool dfs( int day , int cc , int l , int f , int c , int cnt ){ // 天数,我的自信,我的等级,我的嘲讽能力,大佬的自信,还能怼几次
if( cnt < 0 ) return 0 ; //不合法状态先去掉
if( !c || fg ) return 1 ; //如果大佬死了,直接return 1
cc -= a [day + 1] ;
if( cc < 0 || c < 0 ) return 0 ; //注意要先掉HP在看是否小于0 ——> 仔细读题
if( day == n ) return c == 0 ;
//注意边界判断的顺序
// printf( "%ld %ld %ld %ld %ld %ld\n" , day , cc , l , f , c , cnt ) ;
fg |= dfs( day + 1 , cc , l , f , c - 1 , cnt ) ;
fg |= dfs( day + 1 , min( cc + w [day + 1] , k ) , l , f , c , cnt ) ;
fg |= dfs( day + 1 , cc , l + 1 , f , c , cnt ) ;
fg |= dfs( day + 1 , cc , l , f * l , c , cnt ) ;
fg |= dfs( day + 1 , cc , 0 , 1 , c - f , cnt - 1 ) ;
return fg ;
}
void work(){
for( R i = 1 ; i <= m ; i ++ )
fg = 0 , printf( "%d\n" , dfs( 0 , k , 0 , 1 , c [i] , 2 ) ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
正解:\(dp\)+\(bfs\)剪枝。
孩子调了一整天,自闭了。
第一步必须要知道我们的目的是空余出来最多的时间来打大佬,因为活下来和打大佬之间不影响。
首先\(dp\)定义\(i\)天刷\(j\)次水题能空出来多少天
刷表转移(边界事比较少)
\(f[i+1][j-a[i]]=max(f[i+1][j-a[i]],f[i][j]+1)\)
\(f[i+1][min(j-a[i]+w[i],mc)]=max(f[i+1][min(j-a[i]+w[i],mc)],f[i][j])\)
对所有的状态取\(max\)就是最多可以空出来多少天。
之后我们需要知道打出来一个嘲讽需要多少天,能嘲讽多少(也就是求所有的二元组\((F,day)\))
这个暴力\(bfs\),利用其先搜索到的天数是最小的性质。
只不过干搜的话时间开销太大了。
所以考虑用\(Hash\)对三元组\((day,F,L)\)判重,搜过的就不搜了。
同样写一个\(Hash\)对得到的二元组\((F,day)\)判重,有过的\(F\)只记录第一次搜到的\(day\)(因为最小)
这里不能用一个数组解决是因为空间开不起,\(Hash\)约等于自带离散化的数组。
手写\(Hash\),\(Hash\)函数越麻烦几乎越没事(可以多拿几组本地数据试试)
搜完二元组之后开始打大佬,一次零次都很好判断。
主要是打两次。
由于
一个状态可以击败大佬,当且仅当同时满足以上两个条件。
所以我们把\(F\)排序之后,\(i\)从头扫,\(j\)从尾扫,可以发现当\(i\)上升时,满足第一个条件\(j\)是单调不减的。
但满足第二个条件的\(j\)并不单调。
所以在单调扫指针的时候,要对\(F[i]-Day[i]\)取前缀\(max\)(这个我是真的没想到,咕咕咕)
注意有的\(bfs\)的时候攻击力可能变成负的,用\(1ll\)和特判解决一下(\(\#define \ int \ long \ long\)自动忽视)
记录一下单调指针技巧,一般如果可以保证算法没问题就是需要优化的时候可以想这种思路靠拢,实在不行考虑打个表寻找规律。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#define int long
#define R register int
#define printf Ruusupuu = printf
using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
const int N = 1e2 + 10 ;
const int M = 2e7 + 10 ;
const int K = 1e6 + 10 ;
int Ruusupuu ;
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 ^ '0' ) , ch = getchar() ;
return fg ? -w : w ;
}
int n , m , k , day , a [N] , w [N] , c [N] , f [N][N] , top , mx , mem [K] , meme [K] ; bool fg = 0 ;
struct S{ int day , f , l ; S(){} S( int _day , int _f , int _l ){ day = _day , f = _f, l = _l ; } } t ;
struct F{ int d , f ; F(){} F( int _d , int _f ){ d = _d , f = _f ; } } g [M] ;
inline bool cmp( F a , F b ){ return a.f < b.f ; }
inline bool operator == ( S a , S b ){
return a.day == b.day && a.f == b.f && a.l == b.l ;
}
struct HS{
int head [K] , mod , pr1 , pr2 , pr3 , cnt ;
struct E{ S w ; int next ; } a [K << 4] ;
HS(){
memset( head , -1 , sizeof( head ) ) ;
cnt = 0 , pr1 = 20050913 , pr2 = 998244353 , pr3 = 1e9 + 7 ; mod =1000003 ;
}
inline int tr( S a ){
int x = a.day ; x = 1ll * x * pr1 % mod ;
int z = a.f ; z = 1ll * z * pr2 % mod ;
int t = a.l ; t = 1ll * t * pr3 % mod ;
return ( 0ll + x + z + t ) % mod ;
}
inline bool operator [] ( S x ){
if( x.f < 0 ) return 0 ;
int t = tr( x ) ;
// printf( "BOOM%ld %ld %ld %ld %ld\n" , x.day , x.f , x.l , t , head [t] ) ;
for( R i = head [t] ; ~i ; i = a [i].next ){
// printf( "%ld\n" , i ) ;
if( a [i].w == x ) return 1 ;
}
return 0 ;
}
inline void add( S x ){
int t = tr( x ) ;
// printf( "%ld %ld %ld %ld\n" , x.day , x.f , x.l , t ) ;
if( cnt + 2 <= ( K << 3 ) || 1 ){
a [++ cnt].w = x ;
a [cnt].next = head [t] ;
head [t] = cnt ;
mem [t] ++ ;
}
// if( mem [t] % (int) 1e3 == 0 )
// printf( "CNT%ld\n" , mem [t] ) ;
}
} mp ;
struct HSS{
int head [K] , mod , pr1 , pr2 , pr3 , cnt ;
struct E{ int w ; int next ; } a [K << 4] ;
HSS(){
memset( head , -1 , sizeof( head ) ) ;
cnt = 0 , pr1 = 20050913 , pr2 = 998244353 , pr3 = 1e9 + 7 ; mod = 1000003 ;
}
inline int tr( int a ){
int x = a ; x = 1ll * x * pr1 % mod ;
return x ;
}
inline bool operator [] ( int x ){
int t = tr( x ) ;
// printf( "%ld %ld %ld %ld\n" , x.day , x.f , x.l , t ) ;
for( R i = head [t] ; ~i ; i = a [i].next )
if( a [i].w == x ) return 1 ;
return 0 ;
}
inline void add( int x ){
int t = tr( x ) ;
if( cnt + 2 <= ( K << 3 ) || 1 ){
a [++ cnt].w = x ;
a [cnt].next = head [t] ;
head [t] = cnt ;
}
}
} mpp ;
void sc(){
n = read() , m = read() , k = read() ;
for( R i = 1 ; i <= n ; i ++ ) a [i] = read() ;
for( R i = 1 ; i <= n ; i ++ ) w [i] = read() ;
for( R i = 1 ; i <= m ; i ++ ) c [i] = read() , mx = max( mx , c [i] ) ;
}
void pre(){
for( R i = 1 ; i <= n ; i ++ ){
for( R j = a [i] ; j <= k ; j ++ ){
f [i + 1][j - a [i]] = max( f [i + 1][j - a [i]] , f [i][j] + 1 ) ;
f [i + 1][min( k , j - a [i] + w [i] )] = max( f [i + 1][min( k , j - a [i] + w [i] )] , f [i][j] ) ;
}
}
for( R i = 1 ; i <= n ; i ++ ) for( R j = 1 ; j <= k ; j ++ ) day = max( day , f [i][j] ) ;
// printf( "DAY%ld\n" , day ) ;
}
queue< S > q ;
void bfs(){
q.push( S( 2 , 1 , 2 ) ) ;
while( !q.empty() ){
t = q.front() ; q.pop() ;
int dy = t.day , ff = t.f , ll = t.l ;
if( ff > mx ) continue ;
if( day == dy ) continue ;
// printf( "%ld %ld %ld %ld\n" , dy , ff , ll , q.size() ) ;
if( ff > 1 && !mpp [ff] ) g [++ top] = F( dy + 1 , ff ) , mpp.add( ff ) ;
// puts( "shit1" ) ;
// printf( "%ld %ld %ld %ld\n" , ll * ff , ll , ff , mx ) ;
if( !mp[S( dy + 1 , ff * ll , ll )] && 1ll * ff * ll <= (L) mx )
mp.add( S( dy + 1 , ff * ll , ll ) ) , q.push( S( dy + 1 , ff * ll , ll ) ) ;
// puts( "shit2" ) ;
if( !mp[S( dy + 1 , ff , ll + 1 )] && 1ll * ff * ( ll + 1 ) <= (L) mx )
mp.add( S( dy + 1 , ff , ll + 1 ) ) , q.push( S( dy + 1 , ff , ll + 1 ) ) ;
//: puts( "shit3" ) ;
}
}
void work(){
pre() ; bfs() ;
sort( g + 1 , g + 1 + top , cmp ) ;
// printf( "%ld\n" , top ) ;
// puts( "F" ) ;
// for( R i = 1 ; i <= top ; i ++ ) printf( "%ld %ld\n" , g [i].f , g [i].d ) ;
for( R i = 1 ; i <= m ; i ++ ){
fg = 0 ;
if( day >= c [i] ) fg = 1 ;
// printf( "1%d\n" , fg ) ;
else {
for( R j = 1 , k = top ; j <= top ; j ++ ){
if( g [j].f + day - g [j].d >= c [i] && g [j].f <= c [i] ) { fg = 1 ; break ; }
while( ( k >= 1 ) && ( g [j].f + g [k].f > c [i] ) ){
// printf( "%ld %ld %ld %ld %ld %ld\n" , g [j].f , g [j].d , g [k].f , g [k].d , g [j].f + g [k].f , g [j].f + g [k].f + day - g [j].d - g [k].d ) ;
k -- ;
}
if( g [j].f + g [k].f <= c [i] && g [j].f + g [k].f + day - g [j].d - g [k].d >= c [i] ) { fg = 1 ; break ; }
}
}
printf( "%d\n" , fg ) ;
}
}
signed main(){
sc() ;
work() ;
return 0 ;
}