CF1238Div2

https://codeforces.com/contest/1238

(CF1238A~ Prime~ Subtraction)

显然的是如果(y-x>1)那么肯定存在一个质数(p)整除它

这样判一下即可

(CF1238B~Kill~ 'Em All)

肯定要尽可能的把多的人推下去,这样可以减少导弹发射次数,推下去的人越多越好

所以就从右往左发射即可

(CF1238C~Standard ~Free2play)

考虑一个贪心,能不用就尽量不用,然后不得不用的时候就用

然后可以感觉它是对的吧

考虑两个落差比较大的点,比如(100)(5)

会发现(100)(5)的情况等同于(6)(5)的情况

(100)是可以直接到(6)的,这个性质可以简单分类讨论一下得到

考虑一种依赖于值域的(dp)

考虑魔法水晶的使用对于答案的影响

分类讨论得到有一下用法:

(x)时如果(x-1)被选中,那么可以修改(x-1)的状态,然后到达(x-1)

(x)时如果(x-1)未被选中且(x-2)被选中,那么可以修改(x-1)的状态然后到达(x-2),可以发现这一步时无用的,因为它等同于达到(x-1)后前往(x-2)

(x)时如果(x-1)被选中且(x-2)被选中,则可以修改(x-2)后直接达到(x-2),然而实际上这一步与修改(x-1)后前往(x-2)等效

所以可以发现魔法水晶修改的唯一用途是修改(x-1)的状态

所以就可以直接(dp),根据当前位置(i)的状态和(i-1)的初始状态以及(i-2)的初始状态就可以转移了

然而这样复杂度是(O(h))

但是一开始摆出来的性质告诉你相邻两点的(dp)没必要值域的去计算,可以直接将(100)当做(6)去做即可

(CF1238D ~AB-string)

首先可以发现如果(A,B)都出现了超过(2)次那么一定是一个好串

然后实际上不合法的串只有(ABBB...,BBBA...)这类型...

所以就分类讨论即可

(CF1238E ~Keyboard ~Purchase)

非常奇妙的一道状压(dp)

想到状压后特别自闭的想了很久.......

首先是两个点之间的位移次数可以(O(n))预处理出来,于是问题主要在于分配位置上

然后就有一个非常骚的搞法了

我们可以将每个点选/不选状压一下,但是这样肯定是不能维护选取的位置的

可以强制让被选入集合的元素的位置偏小,当然这样是对的,但是这样还需要额外维护每个点的位置复杂度仍然(O(m!))

这样做之后可以将位置看作选入集合的时间,这样一样是对的,于是位置之差就可以转化为时间之差

考虑将两个点之间被选入的时间之差拆开,比如选(a)的时间是(t_a),选(b)的时间为(t_b),那么可以将(|t_b-t_a|)拆开变成(1+1...+1)

你会惊人的发现这个东西是可以累加的,于是每次加点实际上是将集合内所有元素与集合外的所有元素的遍历时间之差都(+1),然后这样(dp)即可

当然要预处理出来每个状态( m ()集合( m S))与其外的元素的位移次数总和即可,这个东西的复杂度是(O(m^2*2^m)),不过跑不满

转移的话就枚举选谁即可,复杂度(O(m*2^m))

复杂度(O(m^2*2^m+n))

#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = s; i <= t; ++ i )
#define re register
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 1e5 + 5 ; 
const int M = 21 ; 
int n, m, f[1 << M], dp[1 << M], cnt[M][M] ;
char s[N] ;
int G( char x ) {
	return x - 'a' ;
}
signed main()
{
	n = gi(), m = gi() ;  
	scanf("%s", s + 1 ) ;
	rep( i, 1, n - 1 ) ++ cnt[G(s[i])][G(s[i + 1])], ++ cnt[G(s[i + 1])][G(s[i])] ;
	int maxn = ( 1 << m ) - 1, t = m - 1 ; 
	rep( i, 1, maxn ) {
		rep( j, 0, t ) {
			if( ( ( 1 << j ) & i ) ) 
			rep( k, 0, t ) {
				if( !( ( 1 << k ) & i ) )
				f[i] += cnt[j][k] ; 
			}
		}
	}
	memset( dp, 63, sizeof(dp) ), dp[0] = 0 ;
	rep( i, 1, maxn ) {
		rep( j, 0, t ) {
			if( ( 1 << j ) & i )
			dp[i] = min( dp[i ^ ( 1 << j )] + f[i], dp[i] ) ;
		}
	}
	printf("%d
", dp[maxn] ) ;
	return 0 ;
}

原文地址:https://www.cnblogs.com/Soulist/p/11656213.html