[折半搜索][随机化][分层图][曼哈顿距离] 集训 Day 13 模拟赛总结

问题在于代码能力不够,码农题写不出来,细节难以处理

以及知识点的欠缺

T3

在这里插入图片描述
在这里插入图片描述
常规背包无法进行,但注意到数据范围较小,考虑 搜索

2 n 2^n 2n 暴力可以拿到 30 30 30 pts,而对于这种 n n n 25 ∼ 40 25\sim40 2540 之间的数据,需要考虑 折半搜索

折半搜索,简单说就是搜索时先搜前一半的状态,将这些状态存进数据结构里,再搜后一半,对于后一半的每个状态,在数据结构中查找与之匹配的最优值,更新答案

这样,时间复杂度由 2 n 2^n 2n 降至 2 n 2 × ( 数据结构维护成本 ) 2^{\frac{n}{2}} \times(数据结构维护成本) 22n×(数据结构维护成本)

本题来说,看到异或和,当然想到 t r i e trie trie

状态匹配时,问题就是:在 t r tr tr 中查找最大 v a l val val ,并且使得在每一位上,当前选择的异或值都不高于 m m m

常规的 t r i e trie trie 套路

#include<bits/stdc++.h>
using namespace std ;

typedef long long LL ;
const int N = 40 ;

int n , m ;
struct nn
{
	int v , w ;
}p[N] ;

int t[1<<22][2] , tot ;
LL maxval[1<<22] ;

void Insert( int x , LL V )
{
	int p = 0 ;
	for(int i = 30 ; i >= 0 ; i -- ) {
		int c = (x>>i)&1 ;
		if( !t[p][c] ) t[p][c] = ++tot ;
		p = t[p][c] ; 
		maxval[p] = max ( maxval[p] , V ) ; 
	}
}

LL quary( int x , LL V ) 
{
	int p = 0 ;
	LL res = 0xcfcfcfcfcfcfcfcf ;
	for(int i = 30 ; i >= 0 ; i -- ) {
		int dm = (m>>i)&1 , dx = (x>>i)&1 ;
		if( dm ) {
			if( dx ) {
				res = max( res , maxval[t[p][1]] ) ;
				p = t[p][0] ;
			}
			else {
				res = max( res , maxval[t[p][0]] ) ;
				p = t[p][1] ;
			}
		}
		else {
			if( dx ) {
				p = t[p][1] ;
			}
			else {
				p = t[p][0] ;
			}
		}
		if( !p ) return res + V ;
	}
	return max( res , maxval[p] + V ) ;
}
LL ans ;

int main()
{
	scanf("%d%d" , &n , &m ) ;
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &p[i].v ) ;
	}
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &p[i].w ) ;
	}
	int len = n / 2 ;
	memset( maxval , 0xcf , sizeof maxval ) ;
	for(int mask = 0 ; mask < (1<<len) ; mask ++ ) {
		int sumV = 0 ;
		LL sumW = 0 ;
		for(int i = 0 ; i < len ; i ++ ) {
			if( mask&(1<<i) ) {
				sumV ^= p[i+1].v ;
				sumW += p[i+1].w ;
			} 
		}
		Insert( sumV , sumW ) ;
	}
	for(int mask = 0 ; mask < (1<<(n-len)) ; mask ++ ) {
		int sumV = 0 ;
		LL sumW = 0 ;
		for(int i = 0 ; i < n-len ; i ++ ) {
			if( mask&(1<<i) ) {
				sumV ^= p[i+len+1].v ;
				sumW += p[i+len+1].w ;
			} 
		}
		ans = max( ans , quary( sumV , sumW ) ) ;
	}
	printf("%lld" , ans ) ;
	return 0 ;  
} 

本题的另一种解法是 随机化

注意到 n n n 非常小,状态数不多,直接随机很大概率能搞出答案

	srand(time(0)) ;
	scanf("%d%d" , &n , &m ) ;
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &p[i].v ) ;
		id[i] = i ;
	}
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &p[i].w ) ;
	}
	while( clock() < 0.9*CLOCKS_PER_SEC ) {// 运行时间 <= 0.9
		random_shuffle( id+1 , id+n+1 ) ;// 打乱 id 数组
		int sumV = 0 ;
		LL sumW = 0 ;
		for(int i = 1 ; i <= n ; i ++ ) {
			sumV ^= p[id[i]].v ;
			sumW += p[id[i]].w ;
			if( sumV <= m ) ans = max( ans , sumW ) ;
		}
	}
    cout << ans ;
	return 0 ;
 } 

考场上真不会 且 状态数不多 的题要大胆搞随机!!

T4

在这里插入图片描述
在这里插入图片描述
观察数据范围,对于 T i = 1 T_i=1 Ti=1 的情况,类比分层图,将一个点拆成:位置点、扫描锁车点、车站1、车站2…

图建出来后直接跑最短路即可

#include<bits/stdc++.h>
using namespace std ;

typedef long long LL ;
const int N = 1e6 + 10 , M = 2e6 + 10 ;

int n , r , s , X , T[N] ;
int inoutsubway[N] , takeother[N] ;
struct nn
{
	int lst , to , val ;
}E[2*M] ;
int head[N] , tot , pit ;
void add( int x , int y , int val )
{
	E[++tot] = (nn){ head[x] , y , val } ;
	head[x] = tot ;
}
map<int,int> mp[N] ; // i 号点,第 j 个铁路站的 节点标号/所属铁路 

LL dis[N] ;
bool vis[N] ;
struct dj
{
	int id ;
	LL val ;
	friend bool operator < ( dj x , dj y ) {
		return x.val > y.val ;
	}
};
priority_queue<dj> q ;
void dij()
{
	memset( dis , 0x3f , sizeof dis ) ;
	dis[1] = 0 ;
	q.push( (dj){ 1 , 0 } ) ;
	while( !q.empty() ) {
		int now = q.top().id ;
		q.pop() ;
		if( vis[now] ) continue ;
		vis[now] = 1 ;
		for(int i = head[now] ; i ; i = E[i].lst ) {
			int t = E[i].to ;
			if( dis[t] > dis[now] + E[i].val ) {
				dis[t] = dis[now] + E[i].val ;
				if( !vis[t] ) q.push( (dj){ t , dis[t] } ) ;
			}
		}
	}
}

int main()
{
	freopen("problem.in","r",stdin) ;
	freopen("problem.out","w",stdout) ;

	scanf("%d%d%d%d" , &n , &r , &s , &X ) ;
	
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &inoutsubway[i] ) ;
		add( i , i+n , X ) ; // 自行车扫锁 
		add( i+n , i , 0 ) ;
	}
	
	for(int i = 1 ; i <= n ; i ++ ) {
		scanf("%d" , &takeother[i] ) ;
	}
	
	int x , y , t ;
	for(int i = 1 ; i <= r ; i ++ ) {
		scanf("%d%d%d" , &x , &y , &t ) ;
		add( x+n , y+n , t ) , add( y+n , x+n , t ) ;
	}
	
	int K , lst ;
	pit = 2*n ;
	for(int i = 1 ; i <= s ; i ++ ) {
		scanf("%d" , &K ) ;
		scanf("%d" , &lst ) ;
		if( !mp[lst][i] ) mp[lst][i] = ++pit ;
		int top = mp[lst][i] ;
		add( lst , top , inoutsubway[lst] ) ;
		add( top , lst , inoutsubway[lst] ) ;
		for(int j = 1 ; j <= K ; j ++ ) {
			scanf("%d%d" , &x , &y ) ;
			if( !mp[y][i] ) mp[y][i] = ++pit ;
			top = mp[y][i] ;
			add( y , top , inoutsubway[y] ) ;
			add( top , y , inoutsubway[y] ) ;
			add( mp[lst][i] , top , x ) ;
			add( top , mp[lst][i] , x ) ; // 铁路双向 
			lst = y ;
		}
		scanf("%d" , &T[i] ) ;
	}
	for(int i = 1 ; i <= n ; i ++ ) { // 处理换乘 
		for( map<int,int> ::iterator it = mp[i].begin() ; it != mp[i].end() ; it ++ ) {
			map<int,int> :: iterator iq = it ;
			if( ++iq == mp[i].end() ) break ;
			for(  ; iq != mp[i].end() ; iq ++ ) {
				add( it->second , iq->second , takeother[i] ) ;
				add( iq->second , it->second , takeother[i] ) ;
			}
		}
	}
	dij() ;
	for(int i = 1 ; i <= n ; i ++ ) {
		printf("%lld " , dis[i] ) ;
	}
	return 0 ;
} 

T5

曼哈顿距离,基于一个式子:

∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣   = m a x (   ∣ ( x 1 + y 1 ) − ( x 2 + y 2 ) ∣   , ∣ ( x 1 − y 1 ) − ( x 2 − y 2 ) ∣   ) \mid x_1-x_2\mid+\mid y_1-y_2\mid \ = max(\ \mid (x_1+y_1)-(x_2+y_2)\mid\ ,\mid (x_1-y_1)-(x_2-y_2)\mid \ ) x1x2+y1y2 =max( (x1+y1)(x2+y2) ,(x1y1)(x2y2) )

在这里插入图片描述
%%% tql tql !!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值