模拟32—「Smooth·Six·Walker」
Smooth
考场上我认为O(KB/Klog(K))过不了,事实是这俩都能过。
先写一个水做法,蚯蚓做法等待填坑吧。
水做法就是恰好搜出来1e7个数然后排序输出,为了恰好1e7个我们需要用暴力优化暴力
搜出来小一点的数,用小一点的数搜更小的数,否则表都打不出来
并不优美的 code
#include <cstdio>
#include <cstring>
#include <assert.h>
#include <algorithm>
#define R register int
#define int long long
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
int Ruusupuu ;
FILE * rsp_5u ;
using namespace std ;
typedef long long L ;
typedef long double D ;
const int N = 1e7 + 10 ;
const D P = 7e7 ;
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 , k , b , top , tot [N] , rs , db [N] , pot ;
int pr [20] = { 0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 } ;
const D M [20] = { 0 , 576460752303423488 , 999502313552216064 , 1000000000000000000 , 1000000000000000000 ,
1000000000000000000 , 1000000000000000000 , 1000000000000000000 , 1000000000000000000 ,
75897663377817600 , 3002995871216640 , 269079750030000 , 44546112408000 , 10803567398778 , 3371226870528 , 1292766707505 } ;
void sc(){
b = read() , k = read() ;
}
void dfs( int x , int mx ){
if( x > M [b] ) return ;
tot [++ top] = x ;
for( R i = mx ; i <= b ; i ++ ){
D pd = (D) x * (D) pr [i] ;
if( pd > M [b] ) break ;
else dfs( x * pr [i] , i ) ;
}
}
void work(){
dfs( 1 , 1 ) ;
stable_sort( tot + 1 , tot + 1 + top ) ;
printf( "%lld\n" , tot [k] ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
Six
神仙题。
56pts可以直接记录队列里面的数然后爆搜。
对于 76pts , 我们发现我们并不关心之前有什么数,只关心之前有什么质因子,所以我们记录在队列里面的质因子集合,这样 \(2^6\) 就可以解决 O(质因子个数) 的问题。
对于满分,就很神仙了。
由于我们只关心他和不和两个数不互质,并不关心和一个数互不互质,所以我们记录 \(f_{s,t}\) 表示当前集合出现的质因子集合为s,出现过的质因子对的集合为t的情况下的方案数。
由于他要和两个数互质,所以质因子对也必须是两个数的(一个数自己的质因子配成的对不算)。
这样,我们只要枚举质因子集合,检查之前有没有出现过质因子对(出现就代表有两个数不互质),就可以判断能不能加入这个数了。
具体的,写出转移方程
\[\large f_{s,t}*sum_s\rightarrow f_{s|\{x\},t|calc(x,s)}
\]
\(calc\) 用来计算x和s中出现的质因子对,\(sum_s\)代表状态s有多少种情况(质因子集合相同的约数等价)。
压36位,一定要用1ll左移,否则会炸int。
code
#include <cmath>
#include <cstdio>
#include <bitset>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>
#include <unordered_map>
#define int long long
#define R register int
#define scanf Ruusupuu = scanf
#define freopen rsp_5u = freopen
int Ruusupuu ;
FILE * rsp_5u ;
using namespace std ;
typedef long long L ;
typedef double D ;
const int N = 1e5 + 10 ;
const int P = 1e9 + 7 ;
inline void debug( int x ){ cout << bitset<50>(x) << endl ; }
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 , p [10] , c [10] , sum [800] , id [800][800] ;
unordered_map < int , int > mp [800] ;
void sc(){
n = read() ;
int s = sqrt( n ) , k = n ;
for( R i = 2 ; i <= s ; i ++ ){
if( k % i == 0 ) p [++ p [0]] = i ;
while( k % i == 0 ) c [p [0]] ++ , k /= i ;
} if( k > 1 ) p [++ p [0]] = k , c [p [0]] ++ ;
for( R i = 1 ; i < ( 1 << p [0] ) ; i ++ )
for( R j = 0 ; j < p [0] ; j ++ )
if( ( i >> j ) & 1 ) sum [i] = ( sum [i] ) ? sum [i] * c [j + 1] : c [j + 1] ;
}
inline void add( int &x , int y ){ x = ( x + y ) >= P ? ( x + y - P ) : ( x + y ) ; }
inline int ti( int x , int y ){ return ( x * y ) % P ; }
inline int mpair( int x ){
int k = 0 ;
for( R i = 0 ; i < p [0] ; i ++ ) for( R j = 0 ; j < p [0] ; j ++ )
if( ( ( x >> i ) & 1 ) && ( ( x >> j ) & 1 ) ){
k |= ( ( 1ll << ( i * p [0] + j ) ) ) ;
k |= ( ( 1ll << ( j * p [0] + i ) ) ) ;
}
return k ;
}
inline int calc( int s , int x ){
int k = 0 ;
for( R i = 0 ; i < p [0] ; i ++ ) for( R j = 0 ; j < p [0] ; j ++ )
if( ( ( s >> i ) & 1 ) && ( ( x >> j ) & 1 ) ) k |= ( ( 1ll << ( i * p [0] + j ) ) | ( 1ll << ( j * p [0] + i ) ) ) ;
return k ;
}
int dfs( int s , int t ){
if( mp [s].find( t ) != mp [s].end() ) return mp [s][t] ;
int k = 1 ;
for( R i = 1 ; i < ( 1 << p [0] ) ; i ++ ){
int r = mpair( i ) ;
if( r & t ) continue ;
add( k , ti( dfs( s | i , t | calc( s , i ) ) , sum [i] ) ) ;
} return mp [s][t] = k ;
}
void work(){
printf( "%lld\n" , dfs( 0 , 0 ) - 1 ) ;
}
signed main(){
sc() ;
work() ;
return 0 ;
}
Walker
按照题意,随机选两个人解出来check,有1/4正确
所以只要检验的足够多就没事
random_shuffle一下,随便检验几组其实就对了。
可以手解,也可以高斯消元,主要就是把 scale*sin/cos 看成一个变量。
还有记录一下acos,asin反三角函数。
返回这个三角函数值对应的弧度,且只返回正数。
所以,利用sin值可以知道这个弧度本来改正还是负,然后输出判断后的acos就可以了。
总结
本场又炸了,原因如下
- T1 写出klogk本地1.3s不太放心,然后乱搞废了许多时间(甚至7:50写完,9:00开的T2)。
- T2 看着有点麻烦就没想暴力,其实随便一个dfs就56pts,枚举数换成枚举集合就76pts,主要没打暴力的原因是没看透序列最多12个数,这个其实想一想就能发现,然而心态急躁什么都想不出来,想出来这个之后算一算复杂度56就有了。
- T3 考场上想到随机化一下肯定能水过,但是考场后期一直很着急,没有推出来柿子(把式子搞乱了),然后就爆炸了。
一些收获
- T1属实没啥收获,等我把蚯蚓的坑填了也许会有收获吧
- T2又是个神仙状压dp,直接按照题意定义质因子对数,然后减少复杂度
- T2这种压36位的状压dp,要构造一个数的时候要用
1ll<<balabala位,否则会爆int - T2告诉我们不要害怕状态数很多,只要合法状态很少,记忆话搜索就可以解决
- T3考场上害怕高斯消元然后想手解,结果没有解出来就炸了,高斯消元我模板不熟,等填上炼金术士之后好好学一学。
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号