Loading

模拟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就可以了。

总结

本场又炸了,原因如下

  1. T1 写出klogk本地1.3s不太放心,然后乱搞废了许多时间(甚至7:50写完,9:00开的T2)。
  2. T2 看着有点麻烦就没想暴力,其实随便一个dfs就56pts,枚举数换成枚举集合就76pts,主要没打暴力的原因是没看透序列最多12个数,这个其实想一想就能发现,然而心态急躁什么都想不出来,想出来这个之后算一算复杂度56就有了。
  3. T3 考场上想到随机化一下肯定能水过,但是考场后期一直很着急,没有推出来柿子(把式子搞乱了),然后就爆炸了。

一些收获

  1. T1属实没啥收获,等我把蚯蚓的坑填了也许会有收获吧
  2. T2又是个神仙状压dp,直接按照题意定义质因子对数,然后减少复杂度
  3. T2这种压36位的状压dp,要构造一个数的时候要用 1ll<<balabala位,否则会爆int
  4. T2告诉我们不要害怕状态数很多,只要合法状态很少,记忆话搜索就可以解决
  5. T3考场上害怕高斯消元然后想手解,结果没有解出来就炸了,高斯消元我模板不熟,等填上炼金术士之后好好学一学。
posted @ 2021-08-08 19:32  Soresen  阅读(30)  评论(0)    收藏  举报