星空
又是本弱想不到的题。
问题经过几次转化,逐渐变得可做,然鹅我根本康不粗来如何转化。
第一步,转区间翻转为两点修改。
这个直接差分一下就行了,还是挺好想到的。
有一个细节,如果我们只是简简单单的把原序列差分,例如
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\)位整数。
而long被C++描述为\(32\)位整数。
所以我习惯#define int long。
但是\(luogu\)的评测机是\(64\)位的(\(noilinux\)没有问题,是\(32\)位的),就把long编译成了\(64\)位整数。
然后我的memset( dis , 0x3f , sizeof( dis ) ) ; 里面初始化的值就不等于0x3f3f3f3f了。
所以,总上所述,我们还是不要使用\(memset\)了,直接手动赋值成0x3f3f3f3f即可(亲测register int的\(for\)跑的比memset快)
然后\(Bfs\)之完后,我们已经得到了点之间的转化关系,直接敲个状压\(dp\)模板就行了。
不过你发现敲完模板之后被出题人骂了一顿
确实,复杂度可以掉一个\(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 ;
}


浙公网安备 33010602011771号