纪中国庆10.4做题小结
T1:教主的花园
Description
教主有着一个环形的花园,他想在花园周围均匀地种上n棵树,但是教主花园的土壤很特别,每个位置适合种的树都不一样,一些树可能会因为不适合这个位置的土壤而损失观赏价值。
教主最喜欢3种树,这3种树的高度分别为10,20,30。教主希望这一圈树种得有层次感,所以任何一个位置的树要比它相邻的两棵树的高度都高或者都低,并且在此条件下,教主想要你设计出一套方案,使得观赏价值之和最高。
Input
输入的第1行为一个正整数n,表示需要种的树的棵树。
接下来n行,每行3个不超过10000的正整数ai,bi,ci,按顺时针顺序表示了第i个位置种高度为10,20,30的树能获得的观赏价值。
第i个位置的树与第i+1个位置的树相邻,特别地,第1个位置的树与第n个位置的树相邻。
Output
输出仅包括一个正整数,为最大的观赏价值和。
Sample Input
4
1 3 2
3 1 2
3 1 2
3 1 2
Sample Output
11
Hint
【样例说明】
第1~n个位置分别种上高度为20,10,30,10的树,价值最高。
【数据规模】
对于20%的数据,有n≤10;
对于40%的数据,有n≤100;
对于60%的数据,有n≤1000;
对于100%的数据,有4≤n≤100000,并保证n一定为偶数。
简要思路:这题其实是一个要考虑后效性的环形 d p dp dp,直观上来看,本题样例的答案是这样得出的:
我用 d p [ i ] [ j ] [ 0 / 1 ] [ k ] dp[i][j][0/1][k] dp[i][j][0/1][k]来表示该状态下的最大价值,其中 i i i表示种到第 i i i棵树, j j j表示树的高度为10,20,30(用1,2,3表示),0,1分别表示当前树的高度比上一棵树小或大,而 k k k记录第一棵树的高度,用于处理环。我这种方法要手动处理前两棵树的每一种情况。 d p dp dp具体的转移不难,但要分类讨论。
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
using namespace std;
int n;
int dp[N][4][2][4];
int val[N][4];
template < typename T >
inline void read( T & res ) {
res = 0;
T pd = 1;
char aa = getchar();
while ( aa < '0' || aa > '9' ) {
if ( aa == '-' ) {
pd = -pd;
}
aa = getchar();
}
while ( aa >= '0' && aa <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
aa = getchar();
}
res *= pd;
return;
}
int main () {
read(n);
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = 1 ; j <= 3 ; ++j ) {
read(val[i][j]);
}
}
dp[2][1][0][2] = val[1][2] + val[2][1];
dp[2][1][0][3] = val[1][3] + val[2][1];
dp[2][3][1][1] = val[1][1] + val[2][3];
dp[2][3][1][2] = val[1][2] + val[2][3];
dp[2][2][1][1] = val[1][1] + val[2][2];
dp[2][2][0][3] = val[1][3] + val[2][2];
for ( int i = 2 ; i <= n - 1 ; ++i ) {
if ( i == n - 1 ) {//最后一棵树的高度受到第一棵树高度的限制,要特判。
for ( int j = 1 ; j <= 3 ; ++j ) {
switch(j) {
case 1 : {
for ( int k = 1 ; k <= 1 ; ++k ) {
dp[i + 1][2][1][k] = max( dp[i + 1][2][1][k] , dp[i][j][0][k] + val[i + 1][2] );
}
for ( int k = 1 ; k <= 2 ; ++k ) {
dp[i + 1][3][1][k] = max( dp[i + 1][3][1][k] , dp[i][j][0][k] + val[i + 1][3] );
}
break;
}
case 3 : {
for ( int k = 2 ; k <= 3 ; ++k ) {
dp[i + 1][1][0][k] = max( dp[i + 1][1][0][k] , dp[i][j][1][k] + val[i + 1][1] );
}
for ( int k = 3 ; k <= 3 ; ++k ) {
dp[i + 1][2][0][k] = max( dp[i + 1][2][0][k] , dp[i][j][1][k] + val[i + 1][2] );
}
break;
}
case 2 : {
for ( int k = 2 ; k <= 3 ; ++k ) {
dp[i + 1][1][0][k] = max( dp[i + 1][1][0][k] , dp[i][j][1][k] + val[i + 1][1] );
}
for ( int k = 1 ; k <= 2 ; ++k ) {
dp[i + 1][3][1][k] = max( dp[i + 1][3][1][k] , dp[i][j][0][k] + val[i + 1][3] );
}
break;
}
}
}
} else {
for ( int j = 1 ; j <= 3 ; ++j ) {
switch(j) {
case 1 : {
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][2][1][k] = max( dp[i + 1][2][1][k] , dp[i][j][0][k] + val[i + 1][2] );
}
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][3][1][k] = max( dp[i + 1][3][1][k] , dp[i][j][0][k] + val[i + 1][3] );
}
break;
}
case 3 : {
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][1][0][k] = max( dp[i + 1][1][0][k] , dp[i][j][1][k] + val[i + 1][1] );
}
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][2][0][k] = max( dp[i + 1][2][0][k] , dp[i][j][1][k] + val[i + 1][2] );
}
break;
}
case 2 : {
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][1][0][k] = max( dp[i + 1][1][0][k] , dp[i][j][1][k] + val[i + 1][1] );
}
for ( int k = 1 ; k <= 3 ; ++k ) {
dp[i + 1][3][1][k] = max( dp[i + 1][3][1][k] , dp[i][j][0][k] + val[i + 1][3] );
}
break;
}
}
}
}
}
int ans = 0;
for ( int i = 1 ; i <= 3 ; ++i ) {
for ( int j = 0 ; j <= 1 ; ++j ) {
for ( int k = 1 ; k <= 3 ; ++k ) {
ans = max( dp[n][i][j][k] , ans );
}
}
}
printf("%d",ans);
return 0;
}
/*
6
1 3 2
3 1 2
3 1 2
3 1 2
1 1 1
5 1 5
*/
T2:教主的游乐场
Description
Orz教主的成员为教主建了一个游乐场,在教主的规划下,游乐场有一排n个弹性无敌的跳跃装置,它们都朝着一个方向,对着一个巨大的湖,当人踩上去装置可以带你去这个方向无限远的地方,享受飞行的乐趣。但是等这批装置投入使用时,却发现来玩的人们更喜欢在这些装置上跳来跳去,并且由于这些装置弹性的优势,不但它们能让人向所对的方向能跳很远,也都能向相反方向跳一定的距离。
于是教主想出了个游戏,这n个装置按朝向相反的方向顺序以1…n编号。第i个装置可以跳到1…i-1个装置,且每个装置有一个不一定相同的反方向弹性a[i],代表第i个装置还可以跳到第i+1…i+a[i]个装置。教主指定一个起始的装置,问从这个装置开始,最少需要连续踩几次装置(起始的装置也算在内),可以跳到第n个装置的后方,即若第k个装置有k+a[i]>n,那么从第k个装置就可以跳到第n个装置的后方。
(PS:你可以认为有n+1个装置,即需要求多少次能条到第n+1个装置)
Input
输入的第1行包含两个正整数n,m,为装置的数目以及询问的次数。
第2行包含n个正整数,第i个正整数为a[i],即第i个装置向反方向最大跳跃的长度。
第3行包含了m个正整数,为询问从哪一个装置开始,最少要几次跳到第n个的后方。
数字之间用空格隔开。
Output
输出包含1行,这一行有m个正整数,对于每一个询问,输出最少需要踩的装置数,数字之间用空格隔开。
行末换行且没有多余的空格。
Sample Input
5 5
2 4 1 1 1
1 2 3 4 5
Sample Output
2 1 2 2 1
Hint
【样例说明】
若从第1个装置开始则跳到第2个装置,接着就可以跳到第n个装置的后方。
若从第3个装置开始则同样跳到第2个装置。
若从第4个装置开始可以跳到第2个装置或最后一个装置,接着跳出第n个装置,答案同样为2。
【数据规模】
对于20%的数据,有n≤10;
对于40%的数据,有n≤100,m≤10;
对于60%的数据,有n≤1000,a[i]≤1000,m≤500;
对于100%的数据,有n≤100000,a[i]≤n,m≤50000。
简要思路:这题有个贪心做法,就是从前往后,找到第一个能跳到最外边(或大于期望最大值)的点,那么这个点右边的点要么自己也能跳到目标位置,要么跳到这个点上再跳到目标位置。这种做法可不断缩小问题规模,在随机数据下表现优秀,但现在在JZOJ上会被卡,只有90分了。
//90分
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
using namespace std;
int n , m;
int a[N] , dis[N];
template < typename T >
inline void read( T & res ) {
res = 0;
T pd = 1;
char aa = getchar();
while ( aa < '0' || aa > '9' ) {
if ( aa == '-' ) {
pd = -pd;
}
aa = getchar();
}
while ( aa >= '0' && aa <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
aa = getchar();
}
res *= pd;
return;
}
int main () {
//freopen( "data8.in" , "r" , stdin );
//freopen( "8.out" , "w" , stdout );
read(n);
read(m);
for ( int i = 1 ; i <= n ; ++i ) {
read(a[i]);
}
int end = n + 1;
dis[end] = 0;
int tem;
while ( end > 1 ) {
for ( int i = 1 ; i <= end - 1 ; ++i ) {
if ( a[i] + i >= end ) {
tem = i;
dis[tem] = dis[end] + 1;
break;
}
}
for ( int i = tem + 1 ; i <= end - 1 ; ++i ) {
if ( a[i] + i >= end ) {
dis[i] = dis[end] + 1;
} else {
dis[i] = dis[tem] + 1;
}
}
end = tem;
}
bool fir = true;
int x;
for ( int i = 1 ; i <= m ; ++i ) {
read(x);
if ( fir ) {
fir = false;
} else {
printf(" ");
}
printf("%d",dis[x]);
}
printf("\n");
return 0;
}
这题其实可以转化为一个区间覆盖问题,一开始只考虑一个点向右跳的情况,从右到左处理点,用线段树维护每个点向右跳的最小步数,每当处理一个新点时,如果它不能直接跳到外界,就查询 [ x , x + a [ x ] ] [x,x+a[x]] [x,x+a[x]]的最小值,它的最小值为查到的最小值加一。最后考虑可以向左跳的情况,从左到右存贮所查到的点的最小值,按照循环顺序更新右边点的答案即可。
//100分
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100005
using namespace std;
int n , m;
int a[N] , front[N] , dis[N];
struct segmenttree{
int val[4 * N];
inline void insert( int pos , int l , int r , int p , int v ) {
if ( l == r ) {
val[pos] = v;
return;
}
int mid = ( l + r ) >> 1;
if ( p <= mid ) {
insert( pos << 1 , l , mid , p , v );
} else {
insert( ( pos << 1 ) + 1 , mid + 1 , r , p , v );
}
val[pos] = min( val[pos << 1] , val[( pos << 1 ) + 1] );
return;
}
inline int query( int pos , int l , int r , int x , int y ) {
if ( l >= x && r <= y ) {
return val[pos];
}
int mid = ( l + r ) >> 1;
int min1 , min2;
min1 = min2 = 0x3f3f3f3f;
if ( x <= mid ) {
min1 = query( pos << 1 , l , mid , x , y );
}
if ( y >= mid + 1 ) {
min2 = query( ( pos << 1 ) + 1 , mid + 1 , r , x , y );
}
return min( min1 , min2 );
}
}tree;
template < typename T >
inline void read( T & res ) {
res = 0;
T pd = 1;
char aa = getchar();
while ( aa < '0' || aa > '9' ) {
if ( aa == '-' ) {
pd = -pd;
}
aa = getchar();
}
while ( aa >= '0' && aa <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
aa = getchar();
}
res *= pd;
return;
}
int main () {
read(n);
read(m);
for ( int i = 1 ; i <= n ; ++i ) {
read(a[i]);
}
memset( tree.val , 0x3f , sizeof( tree.val ) );
if ( a[n] >= 1 ) { //这是废话
front[n] = 1;
}
tree.insert( 1 , 1 , n , n , front[n] );
for ( int i = n - 1 ; i >= 1 ; --i ) {
if ( a[i] + i > n ) {
front[i] = 1;
} else {
front[i] = tree.query( 1 , 1 , n , i + 1 , i + a[i] ) + 1;
}
tree.insert( 1 , 1 , n , i , front[i] );
}
int minf = 0x3f3f3f3f;
for ( int i = 1 ; i <= n ; ++i ) {
dis[i] = min( front[i] , minf + 1 );
minf = min( minf , dis[i] );
}
bool fir = true;
int x;
for ( int i = 1 ; i <= m ; ++i ) {
read(x);
if ( fir ) {
fir = false;
} else {
printf(" ");
}
printf("%d",dis[x]);
}
printf("\n");
return 0;
}
T3:教主的集合序列
Description
定义集合S1为1到n之间所有正整数组成的集合,即S1={1,2,3…n}。当k>1时,Sk为集合Sk-1中任意两个不相同数之和的集合。
例如,当n=3时:
S1={1,2,3}
S2={3,4,5}
S3={7,8,9}
S4={15,16,17}
……
现将每个集合中所有元素取出,集合Si的数放在集合Si+1的数的前面,同一个集合数从小到大排序,这样得到一个序列L。例如,当n=3时,L=1,2,3,3,4,5,7,8,9,15,16,17……
那么,现对于给定的n和k,求L中第k个数。
Input
输入包含1行,为2个正整数n和k,两个整数用空格隔开。
Output
输出包含1行,为一个正整数,即序列L中第k个数。若这个数不存在,则输出-1。
Sample Input
4 6
Sample Output
4
Hint
【样例说明】
当n=4时,序列L=1,2,3,4,3,4,5,6,7,7……
所以序列中第6个数为4。
【数据规模】
对于20%的数据,有k≤20;
对于40%的数据,有k≤10000,n≤5;
对于50%的数据,满足答案不超过2^31-1;
对于60%的数据,有k≤2^30-1;
对于80%的数据,有k≤10^100;
对于100%的数据,有k≤10^1000,1<n≤1000,对于n≤3有k≤90000。
简要思路:当n = 1或2时,集合的总数是有限的,总共为
1
{1}
1以及
1
,
2
,
3
{1,2,3}
1,2,3,询问的范围超了就可以直接打出"-1"。根据题意,不难发现每个集合中的数都是连续的,因此集合可用区间表示。当n = 3时,观察可得集合的元素总数不变,对于第k个集合,第一个数为
2
k
−
1
2^k-1
2k−1(自己证),第二三个为
2
k
2^k
2k,
2
k
+
1
2^k+1
2k+1。
当
n
≥
4
n \ge 4
n≥4时,第
k
k
k个集合区间长度为
2
k
−
1
∗
(
n
−
3
)
+
3
2^{k-1}*(n-3)+3
2k−1∗(n−3)+3,表示为
[
2
k
−
1
,
2
k
−
1
∗
(
n
−
1
)
+
1
]
[2^k-1,2^{k - 1}*(n - 1) + 1]
[2k−1,2k−1∗(n−1)+1](用到等比数列求和公式推导的)。实现要靠高精度(本题难点所在)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 10000000
#define N 1010
#define M 8100
#define ll long long
using namespace std;
ll n , m;
ll ans[3][4];
ll num[M];
char s[N];
template < typename T >
inline void read( T & res ) {
res = 0;
T pd = 1;
char aa = getchar();
while ( aa < '0' || aa > '9' ) {
if ( aa == '-' ) {
pd = -pd;
}
aa = getchar();
}
while ( aa >= '0' && aa <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
aa = getchar();
}
res *= pd;
return;
}
inline void write( ll tem[] ) {
printf("%lld",tem[tem[0]]);
for ( int i = tem[0] - 1 ; i >= 1 ; --i ) {
printf("%07lld",tem[i]);
}
return;
}
inline int cmp( ll a[] , ll b[] ) { // 2 : a = b 0 : a < b 1 : a > b
if ( a[0] != b[0] ) {
return (a[0] > b[0]);
}
for ( int i = a[0] ; i >= 1 ; --i ) {
if ( a[i] != b[i] ) {
return (a[i] > b[i]);
}
}
return 2;
}
inline void mul( ll a[] , ll b[] , ll c[] ) {
//memset( c , 0 , sizeof(c) );
ll t[M] = {0};
t[0] = a[0] + b[0];
for ( int i = 1 ; i <= a[0] ; ++i ) {
for ( int j = 1 ; j <= b[0] ; ++j ) {
t[i + j - 1] += a[i] * b[j];
}
}
for ( int i = 1 ; i <= t[0] ; ++i ) {
t[i + 1] += t[i] / mod;
t[i] %= mod;
}
while ( !t[t[0]] && t[0] > 1 ) {
--t[0];
}
memcpy( c , t , sizeof(t) );
return;
}
inline void qser( ll tem[] , ll x ) {
//memset( tem , 0 , sizeof(tem) );
ll t[M] = {0};
t[0] = 1;
t[1] = 2;
tem[0] = tem[1] = 1;
while ( x ) {
if ( x & 1 ) {
mul( tem , t , tem );
}
mul( t , t , t );
x >>= 1;
}
return;
}
inline void summ( ll a[] , ll b[] , ll c[] ) {
//memset( c , 0 , sizeof(c) );
ll t[M] = {0};
t[0] = max( a[0] , b[0] );
for ( int i = 1 ; i <= t[0] ; ++i ) {
t[i] = a[i] + b[i];
}
for ( int i = 1 ; i <= t[0] ; ++i ) {
t[i + 1] += t[i] / mod;
t[i] %= mod;
}
while ( t[t[0] + 1] ) {
++t[0];
}
memcpy( c , t , sizeof(t) );
return;
}
inline void minu( ll a[] , ll b[] , ll c[] ) {
//memset( c , 0 , sizeof(c) );
ll t[M] = {0};
t[0] = a[0];
for ( int i = 1 ; i <= t[0] ; ++i ) {
t[i] = a[i] - b[i];
}
for ( int i = 1 ; i <= t[0] - 1 ; ++i ) {
if ( t[i] < 0 ) {
--t[i + 1];
t[i] += mod;
}
}
while ( !t[t[0]] && t[0] > 1 ) {
--t[0];
}
memcpy( c , t , sizeof(t) );
return;
}
inline void trans( char ss[] , ll a[] ) {
int l = strlen(ss);
a[0] = 1;
int tem;
for ( tem = l ; tem > 6 ; tem -= 7 ) {
for ( int j = 0 ; j <= 6 ; ++j ) {
a[a[0]] = a[a[0]] * 10 + ( ss[tem - 7 + j] - '0' );
}
++a[0];
}
for ( int j = 0 ; j < tem ; ++j ) {
a[a[0]] = a[a[0]] * 10 + ( ss[j] - '0' );
}
return;
}
inline void cal( ll a[] , ll i , ll b[] ) {
qser( a , i - 1 );
b[1] = n - 3;
mul( a , b , a );
b[1] = i * 3;
summ( a , b , a );
b[1] = n;
minu( a , b , a );
return;
}
inline void work() {
ll t[M] = {0};
ll tem[M] = {0};
tem[0] = 1;
int l = 1;
int r = 5000;
while ( l < r ) {
int mid = ( l + r + 1 ) >> 1;
cal( t , mid , tem );
int te = cmp( num , t );
if ( te == 2 ) {
qser( t , mid - 2 );
tem[1] = n - 1;
mul( t , tem , t );
tem[1] = 1;
summ( t , tem , t );
write( t );
return;
}
if ( te ) {
l = mid;
} else {
r = mid - 1;
}
}
cal( t , r , tem );
minu( num , t , num );
qser( t , r );
tem[1] = 2;
minu( t , tem , t );
summ( num , t , t );
write(t);
}
int main () {
read(n);
if ( n < 3 ) {
ans[1][1] = 1;
ans[2][1] = 1;
ans[2][2] = 2;
ans[2][3] = 3;
read(m);
if ( m <= 3 && ans[n][m] > 0 ) {
printf("%lld",m);
} else {
printf("-1");
}
return 0;
} else if ( n == 3 ) {
read(m);
qser( num , ( m + 2 ) / 3 );
if ( m % 3 == 1 ) {
--num[1];
}
if ( m % 3 == 0 ) {
++num[1];
}
write(num);
return 0;
} else {
memset( num , 0 , sizeof(num) );
scanf("%s",s);
trans( s , num );
work();
}
return 0;
}
T4 :教主的难题
Description
一个数x可以按以下规则生成数字:
1、将任意两位交换,若交换的数字为a和b,生成的代价为((a and b)+(a xor b))*2。
例如134可以生成431,因为431可以从134的个位(4)与百位(1)交换后得到,代价为((1 and 4)+(1 xor 4))*2=10。
2、将其中一位数删除,但是删除的数要满足大于等于它左边的数,且小等于它右边的数,并且定义最高位左边的数为个位,个位右边的数为最高位。若删除的数字为a,它左边的数为b,它右边的数为c,则生成的代价为a+(b and c)+(b xor c)。
例如212,可以删除个位的数得到21,但是因为2>1,所以1是不能被删除的。特别地,若x为两位数,它能删除当且仅当x的两位数相同,若x为一位数,它是不能被删除的。
3、在两位数字之间,也可以是数字的前面或后面加入一位数,同样地,加入的数要满足大等于它左边的数,且小等于它右边的数,并且定义最高位左边的数为个位,个位右边的数为最高位。若加入的数字为a,它左边的数为b,它右边的数为c,则生成的代价为a+(b and c)+(b xor c)。
例如241,它可以在2与4之间加入一个3,生成2341,也可以在数的末尾加入一个1或者2,当然还有其它可以生成的数,但是不能在4和1之间加入数字。
你的任务是,S一开始为n个不同的给定数组成的集合,每次可以从S中取出一个数生成一个满足生成规则的数加入S中,并且取出的数仍然存在于S中。生成的数的位数不能大于S初始集合最大的数的位数。问在S元素最多的情况下,总代价最小是多少。
Input
输入的第1行为一个正整数n,为集合S初始元素个数。
第2行包含n个正整数a1,a2, …, an,数之间空格隔开,为S中初始元素。
Output
输出包括一个正整数,为最小代价。
Sample Input
2
111 22
Sample Output
12
Hint
【样例说明】
111删除1得到11,代价为2,11删除1得到1,代价为2,同样22删除和加入一个2得到2,222,代价均为4,总代价2+2+4+4=12。111无法生成1111因为111为一个3位数,而1111为一个4位数。
利用交换/添加规则无法让集合元素更多,所以最小代价为12。
【数据规模】
对于20%的数据,有ai<100;
对于40%的数据,有ai<1000;
对于50%的数据,有n=1;
对于60%的数据,有ai<10000;
对于100%的数据,有ai<100000,n≤5,保证的任何一位不包含0。
简要思路:这题通过率最低,但最好讲,对于一个数 x x x,它通过任意一种方式转化为 y y y,可以表示为从 x x x到 y y y连一条价值为转化价值的边,由于所有可得到的点是有限的,再把原有的几个点连上边权为零的边,我们不难得出一个图,由于图比较稀疏,只要用 k r u s k a l kruskal kruskal跑个最小生成树即可。建图的具体操作见代码吧。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n , bcnt , ncnt , maxn;
int num[6];
int vis[100005];
int f[100005];
struct node{
int x;
int y;
int val;
}str[500005];
template < typename T >
inline void read( T & res ) {
res = 0;
T pd = 1;
char aa = getchar();
while ( aa < '0' || aa > '9' ) {
if ( aa == '-' ) {
pd = -pd;
}
aa = getchar();
}
while ( aa >= '0' && aa <= '9' ) {
res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
aa = getchar();
}
res *= pd;
return;
}
inline void add( int x , int y , int val ) {
str[++bcnt].x = x;
str[bcnt].y = y;
str[bcnt].val = val;
return;
}
inline int find( int x ) {
if ( x == f[x] ) {
return x;
} else {
f[x] = find( f[x] );
return f[x];
}
}
inline bool cmp( node a , node b ) {
return a.val < b.val;
}
inline void dfs( int cur ) {
int now[6] , tem[6];
int l = 0;
int t = cur;
while ( t ) {
now[++l] = t % 10;
t /= 10;
}
for ( int i = 1 ; i < l ; ++i ) {
for ( int j = i + 1 ; j <= l ; ++j ) {
for ( int k = 1 ; k <= l ; ++k ) {
tem[k] = now[k];
}
tem[i] = now[j];
tem[j] = now[i];
t = 0;
for ( int k = l ; k >= 1 ; --k ) {
t = t * 10 + tem[k];
}
int val = 2 * ( ( tem[i] & tem[j] ) + ( tem[i] ^ tem[j] ) );
add( cur , t , val );
if ( !vis[t] ) {
vis[t] = 1;
ncnt++;
dfs( t );
}
}
}
if ( l > 1 ) {
for ( int i = 1 ; i <= l ; ++i ) {
int p1 = i + 1;
int p2 = i - 1;
if ( p1 == l + 1 ) {
p1 = 1;
}
if ( p2 == 0 ) {
p2 = l;
}
if ( now[i] >= now[p1] && now[i] <= now[p2] ) {
t = 0;
for ( int k = l ; k >= 1 ; --k ) {
if ( k != i ) {
t = t * 10 + now[k];
}
}
int val = ( now[i] + ( now[p1] & now[p2] ) + ( now[p1] ^ now[p2] ) );
add( cur , t , val );
if ( !vis[t] ) {
vis[t] = 1;
ncnt++;
dfs( t );
}
}
}
}
if ( cur * 10 >= maxn ) {
return;
}
for ( int i = now[1] ; i <= now[l] ; ++i ) {
int val = ( i + ( now[1] & now[l] ) + ( now[1] ^ now[l] ) );
t = cur * 10 + i;
add( cur , t , val );
if ( !vis[t] ) {
vis[t] = 1;
ncnt++;
dfs( t );
}
t = i;
for ( int j = l ; j >= 1 ; --j ) {
t = t * 10 + now[j];
}
add( cur , t , val );
if ( !vis[t] ) {
vis[t] = 1;
ncnt++;
dfs(t);
}
}
for ( int i = 1 ; i < l ; ++i ) {
int p = i + 1;
for ( int j = now[p] ; j <= now[i] ; ++j ) {
int val = ( j + ( now[p] & now[i] ) + ( now[p] ^ now[i] ) );
t = 0;
for ( int k = l ; k >= 1 ; --k ) {
if ( k == i ) {
t = ( t * 10 + j ) * 10 + now[k];
} else {
t = t * 10 + now[k];
}
}
add( cur , t , val );
if ( !vis[t] ) {
vis[t] = 1;
ncnt++;
dfs( t );
}
}
}
return;
}
int main () {
read(n);
maxn = 1;
for ( int i = 1 ; i <= n ; ++i ) {
read(num[i]);
while ( maxn <= num[i] ) {
maxn *= 10;
}
}
for ( int i = 1 ; i <= n ; ++i ) {
for ( int j = i + 1 ; j <= n ; ++j ) {
add( num[i] , num[j] , 0 );
}
}
for ( int i = 1 ; i <= n ; ++i ) {
if ( !vis[num[i]] ) {
vis[num[i]] = 1;
ncnt++;
dfs( num[i] );
}
}
sort( str + 1 , str + 1 + bcnt , cmp );
for ( int i = 1 ; i < maxn ; ++i ) {
f[i] = i;
}
ncnt--;
int ans = 0;
for ( int i = 1 ; i <= bcnt ; ++i ) {
int x = str[i].x;
int y = str[i].y;
int fx = find( x );
int fy = find( y );
if ( fx != fy ) {
ans += str[i].val;
//cout << str[i].val << endl;
f[fx] = fy;
ncnt--;
}
if ( !ncnt ) {
break;
}
}
printf("%d",ans);
return 0;
}