Loading

星空

又是本弱想不到的题。

问题经过几次转化,逐渐变得可做,然鹅我根本康不粗来如何转化。

第一步,转区间翻转为两点修改。
这个直接差分一下就行了,还是挺好想到的。
有一个细节,如果我们只是简简单单的把原序列差分,例如

void sc(){
	n = read() , k = read() , m = read() ;
	for( R i = 1 ; i <= k ; i ++ ) fg [read()] = 1 ;	
	
	for( R i = 1 ; i <= n ; i ++ )
		if( fg [i] ^ fg [i - 1] ) st [++ top] = i ;
		
	for( R i = 1 ; i <= m ; i ++ ) op [i] = read() ; 
	
	for( R i = 1 ; i <= top ; i ++ ){
		bfs( st [i] ) ;
		for( R j = 1 ; j <= top ; j ++ )
			pri [i][j] = dis [st [j]] ;//, printf( "%ld %ld %ld\n" , i , j , pri [i][j] ) ;
	}
}

会有一个问题,就是搞出来之后\(top\)可能是个奇数,到后面就知道\(top\)是奇数有多难受了。
所以这里直接给他跑到n+1,这样就可以保证\(top\)是个偶数了。

现在,我们有\(n\)个节点,有\(2*k\)个点上有\(1\)
我们每次操作,要不选择两点均为\(1\),要不选择\(1\)\(1\)\(1\)\(0\)
这个还是很好想到的,毕竟闲着没事造出来两个\(1\)肯定不是最优解的一部分。

接下来这步比较难想到。
每个节点都可以通过操作转化为其他节点(但不一定是所有节点)。
那么如果我们让一个为\(1\)的节点转化到另一个为\(1\)的节点上,就可以把这两个同时变成\(0\)
我们只需要求出来一个节点转化到另一个节点至少需要几步,或者知道根本不可能。

完全背包可以通过本题,不过被\(Hack\)了。
正解是\(Bfs\),一般求最少的啥的可以考虑\(Bfs\)\(ID-Dfs\),能\(Bfs\)还是最好\(Bfs\),因为相对于\(ID-Dfs\)少了很多重复的搜索。
每个点都可以通过\(m\)个操作转化到其他点上,一共\(n\)个点,单次搜索复杂度上限\(O(nm)\),一共\(2k\)次搜索,复杂度没问题,那么直接搜就行了。

注意一个小问题,虽然int被普遍认为是\(32\)位整数,但是C++只说了他是不小于\(16\)位整数。
longC++描述为\(32\)位整数。
所以我习惯#define int long
但是\(luogu\)的评测机是\(64\)位的(\(noilinux\)没有问题,是\(32\)位的),就把long编译成了\(64\)位整数。
然后我的memset( dis , 0x3f , sizeof( dis ) ) ; 里面初始化的值就不等于0x3f3f3f3f了。
所以,总上所述,我们还是不要使用\(memset\)了,直接手动赋值成0x3f3f3f3f即可(亲测register int\(for\)跑的比memset快)

\[\color\purple{\Huge{\text{手动赋值就完事了}}} \]

\[\color\red{\Huge{\text{手动赋值就完事了}}} \]

\[\color\green{\Huge{\text{手动赋值就完事了}}} \]

然后\(Bfs\)之完后,我们已经得到了点之间的转化关系,直接敲个状压\(dp\)模板就行了。
不过你发现敲完模板之后被出题人骂了一顿

image

确实,复杂度可以掉一个\(k\),不过愤怒的小鸟那道题,我貌似也是直接\(n^2\)的,也过了

这应该是状压\(dp\)的一个常用套路,就是不枚举第一维,而是直接通过找第一个待转移的来代替。

code
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <bitset>
#include <iostream>
#include <assert.h>
#include <queue>
#define mp make_pair
#define R register int
#define int long 
#define printf Ruusupuu = printf

int Ruusupuu ;

using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int Inf = 0x3f3f3f3f ;
const int N = 4e4 + 10 ;
const int M = 1e2 + 10 ;
const int K = 18 ;

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 , m , x , op [M] , st [N] , top , dis [N] , pri [K][K] , f [1 << K] ;
bool fg [N] ;

inline void debug( int x ){ cout << bitset<10> ( x ) << endl ;  }

inline void bfs( int f ){

	queue< int > q ; q.push( f ) ;
	for( R i = 0 ; i <= n + 1 ; i ++ ) dis [i] = Inf ;
	dis [f] = 0 ;
	
	while( !q.empty() ){
		int x = q.front() ; q.pop() ; 
		
		for( R i = 1 ; i <= m ; i ++ ){
			if( x + op [i] <= n + 1 && dis [x + op [i]] == Inf )
				q.push( x + op [i] ) , dis [x + op [i]] = dis [x] + 1 ; 
			
			if( x - op [i] >= 1 && dis [x - op [i]] == Inf )
				q.push( x - op [i] ) , dis [x - op [i]] = dis [x] + 1 ;
		}
	}
}

void sc(){
	n = read() , k = read() , m = read() ;
	for( R i = 1 ; i <= k ; i ++ ) fg [read()] = 1 ;	
	
	for( R i = 1 ; i <= n + 1 ; i ++ )
		if( fg [i] ^ fg [i - 1] ) st [++ top] = i ;
		
	for( R i = 1 ; i <= m ; i ++ ) op [i] = read() ; 
	
	for( R i = 1 ; i <= top ; i ++ ){
		bfs( st [i] ) ;
		for( R j = 1 ; j <= top ; j ++ )
			pri [i][j] = dis [st [j]] ;//, printf( "%ld %ld %ld\n" , i , j , pri [i][j] ) ;
	}
}

void work(){
	for( R i = 1 ; i <= ( 1 << top ) ; i ++ ) f [i] = Inf ;
	for( R i = 0 ; i < ( 1 << top ) ; i ++ ){
		R j = 0 ; while( j < top && ( i >> j ) & 1 ) j ++ ;
		//for( R j = 0 ; j < top ; j ++ ){
			//if( ( i >> j ) & 1 ) continue ;
			for( R k = j + 1 ; k < top ; k ++ ){
				if( ( i >> k ) & 1 ) continue ;
				f [i | ( 1 << j ) | ( 1 << k )] = min( f [i | ( 1 << j ) | ( 1 << k )] , f [i] + pri [j + 1][k + 1] ) ;			
			}
	} printf( "%ld\n" , f [( 1 << top ) - 1] ) ;
}

signed main(){	
	sc() ;
	work() ;
	return 0 ;
}
posted @ 2021-06-27 18:02  Soresen  阅读(42)  评论(0)    收藏  举报