problem
给定 n n n 个在 [ 0 , 2 ω − 1 ] [0,2^\omega-1] [0,2ω−1] 内的整数。执行下面操作两种操作共 n − 1 n-1 n−1 次:
- 选择两个整数
x
,
y
x,y
x,y 从数列中删去,并加入
⌊
x
∣
y
2
⌋
\lfloor\frac{x|y}{2}\rfloor
⌊2x∣y⌋,这里的
|
表示按位或。 - 选择一个整数 x x x 从序列中删去。
不难发现每次操作后拥有的整数数量恰好少一,在 n − 1 n-1 n−1 次操作后你将得到恰好一个整数。
最小化这个整数并输出你的结果。
多组数据, 1 ≤ T ≤ 10 1\le T\le 10 1≤T≤10。
测试点编号 | n | w | 特殊性质 |
---|---|---|---|
$1 ∼ 2 $ | ≤ 6 ≤ 6 ≤6 | ≤ 60 ≤ 60 ≤60 | 无 |
$3 ∼ 5 $ | ≤ 8 ≤ 8 ≤8 | ≤ 60 \le 60 ≤60 | 无 |
6 ∼ 8 6 ∼ 8 6∼8 | ≤ 300 ≤ 300 ≤300 | ≤ 12 ≤ 12 ≤12 | 无 |
9 ∼ 12 9 ∼ 12 9∼12 | ≤ 5000 ≤ 5000 ≤5000 | ≤ 18 ≤ 18 ≤18 | 无 |
13 ∼ 15 13 ∼ 15 13∼15 | ≤ 5000 \le 5000 ≤5000 | ≤ 40 ≤ 40 ≤40 | 无 |
16 ∼ 17 16 ∼ 17 16∼17 | ≤ 1 0 5 ≤10^5 ≤105 | ≤ 60 ≤ 60 ≤60 | A |
18 ∼ 20 18 ∼ 20 18∼20 | ≤ 1 0 5 \le 10^5 ≤105 | ≤ 60 \le 60 ≤60 | 无 |
特殊限制 A:保证所有 a i a_i ai 都可以表示成 2 2 2 的整数次幂减一的形式。
solution
⌊ x ∣ y 2 ⌋ \lfloor\frac{x|y}{2}\rfloor ⌊2x∣y⌋ 的 / 2 /2 /2 可以看作二进制右移一位, ⌊ x ∣ y 2 ⌋ ⇔ ( x ∣ y ) > > 1 ⇔ ( x > > 1 ) ∣ ( y > > 1 ) ⇔ ⌊ x 2 ⌋ ∣ ⌊ y 2 ⌋ \lfloor\frac{x|y}{2}\rfloor\Leftrightarrow (x|y)>>1\Leftrightarrow (x>>1)\big|(y>>1)\Leftrightarrow \lfloor\frac{x}{2}\rfloor\big|\lfloor\frac{y}{2}\rfloor ⌊2x∣y⌋⇔(x∣y)>>1⇔(x>>1)∣∣(y>>1)⇔⌊2x⌋∣∣⌊2y⌋。
可以看到按位或操作后再右移等价于各个元素分别右移后再按位或。
设 c i : a i c_i:a_i ci:ai 参与合并操作的次数 / 右移的位数。
则 a n s = ⌊ a 1 2 c 1 ⌋ ∣ ⌊ a 2 2 c 2 ⌋ ∣ . . . ∣ ⌊ a n 2 c n ⌋ ans=\lfloor\frac{a_1}{2^{c_1}}\rfloor\Big|\lfloor\frac{a_2}{2^{c_2}}\rfloor\Big|...\Big|\lfloor\frac{a_n}{2^{c_n}}\rfloor ans=⌊2c1a1⌋∣∣∣⌊2c2a2⌋∣∣∣...∣∣∣⌊2cnan⌋。
有个显然是对的但又没想到的结论:在仅考虑合并操作的情况下,一组 { c i } \{c_i\} {ci} 合法当且仅当 ∑ i = 1 n 1 2 c i = 1 \sum_{i=1}^{n}\frac{1}{2^{c_i}}=1 ∑i=1n2ci1=1。
加上考虑删除操作的情况,可知一组 { c i } \{c_i\} {ci} 合法当且仅当 ∑ i = 1 n 1 2 c i ≥ 1 \sum_{i=1}^n\frac{1}{2^{c_i}}\ge 1 ∑i=1n2ci1≥1。
因为如果满足和 ≥ 1 \ge 1 ≥1,那么一定存在子集 S ⊂ { 1 , 2 , . . . , n } S\subset\{1,2,...,n\} S⊂{1,2,...,n} 满足 ∑ i ∈ S 1 2 c i = 1 \sum_{i\in S}\frac{1}{2^{c_i}}=1 ∑i∈S2ci1=1,那么不在 S S S 内的就是被删除元素。
换言之, ∑ i = 1 n 1 2 c i \sum_{i=1}^n\frac{1}{2^{c_i}} ∑i=1n2ci1 越大越有可能进入备选答案集合。
case 6~8
基于此结论,我们可以设计一个非常暴力的状态转移方程。
设 f i , j : f_{i,j}: fi,j: 考虑 a [ 1 ∼ i ] a[1\sim i] a[1∼i] 这些数经过合并和删除一系列操作后的按位或为 j j j 的 ∑ k = 1 i 1 2 c k \sum_{k=1}^i\frac{1}{2^{c_k}} ∑k=1i2ck1 的最大值。
直接暴力转移,枚举
a
i
a_i
ai 右移的位数
c
i
c_i
ci。
f
i
,
j
∣
(
a
i
>
>
c
i
)
←
max
f
i
−
1
,
j
f_{i,j|(a_i>>c_i)}\leftarrow^{\max}f_{i-1,j}
fi,j∣(ai>>ci)←maxfi−1,j
状态数
O
(
n
2
ω
)
O(n2^\omega)
O(n2ω),转移
O
(
ω
)
O(\omega)
O(ω),时间复杂度
O
(
T
n
2
ω
ω
)
O(Tn2^\omega\omega)
O(Tn2ωω)。
for( int i = 0;i <= n;i ++ )
for( int j = 0;j < (1 << w);j ++ )
dp[i][j] = -1;
dp[0][0] = 0;
for( int i = 1;i <= n;i ++ )
for( int j = 0;j <= (1 << w);j ++ ) {
dp[i][j] = max( dp[i][j], dp[i - 1][j] );
if( dp[i - 1][j] != -1 )
for( int k = 0;k <= w;k ++ )
dp[i][j | (a[i] >> k)] = max( dp[i][j | (a[i] >> k)], dp[i - 1][j] + 1.0 / (1 << k) );
}
for( int i = 0;i < (1 << w);i ++ )
if( dp[n][i] >= 1 ) { printf( "%lld\n", i ); break; }
case 9~12
考虑对 d p dp dp 进行优化。
考虑答案的上下界。显然答案不会超过 2 ω − ⌊ log 2 n ⌋ 2^{\omega-\lfloor\log_2n\rfloor} 2ω−⌊log2n⌋(最大值与其余的数都进行合并操作)。
状态数 O ( n 2 ω − ⌊ log 2 n ⌋ ) = O ( 2 ω ) O(n2^{\omega-\lfloor\log_2n\rfloor})=O(2^\omega) O(n2ω−⌊log2n⌋)=O(2ω),转移 O ( ω − ⌊ log 2 n ⌋ ) O(\omega-\lfloor\log_2n\rfloor) O(ω−⌊log2n⌋),时间复杂度 O ( T ω 2 ω ) O(T\omega2^\omega) O(Tω2ω)。
int m = 1 << (int)( w - log2( n ) + 1 );
for( int i = 0;i <= m;i ++ ) dp[0][i] = 0;
for( int i = 1;i <= n;i ++ ) {
int d = i & 1;
for( int j = 0;j <= m;j ++ ) dp[d][j] = dp[d ^ 1][j];
for( int j = 0;j <= m;j ++ ) {
for( int k = 0;k <= w;k ++ )
if( ( j | (a[i] >> k) ) <= m )
dp[d][j | (a[i] >> k)] = max( dp[d][j | (a[i] >> k)], dp[d ^ 1][j] + (1 << w - k) );
}
}
for( int i = 0;i <= m;i ++ )
if( dp[n & 1][i] >= (1 << w) ) { printf( "%lld\n", i ); break; }
case 13~15
通过最原始的暴力 d p dp dp 可知,我们能在 O ( n ω ) O(n\omega) O(nω) 的时间内判断一个数 x x x 是否符合成为最后答案的要求。
即 O ( n ) O(n) O(n) 枚举 a i a_i ai, O ( ω ) O(\omega) O(ω) 枚举 c i c_i ci,在满足 ( a i > > c i ) ∣ x = x (a_i>>c_i)\big|x=x (ai>>ci)∣∣x=x 的前提下尽可能减小 c i c_i ci,等价于尽可能增大 1 2 c i \frac{1}{2^{c_i}} 2ci1。
通过 ∑ i = 1 n 1 2 c i ≥ 1 ? \sum_{i=1}^n\frac{1}{2^{c_i}}\ge 1? ∑i=1n2ci1≥1? 来判断 x x x 能否成为候选答案。
所以我们贪心地从高位到低位考虑尽量填 0 0 0。
具体而言,先初始 a n s = 2 ω − 1 ans=2^\omega-1 ans=2ω−1(全 1 1 1),然后 i = ω → 1 i=\omega\rightarrow 1 i=ω→1 顺次考虑 a n s − 2 i ans-2^i ans−2i 是否可行,可行就 a n s − = 2 i ans-=2^i ans−=2i 即可。
时间复杂度 O ( T n ω 2 ) O(Tn\omega^2) O(Tnω2)。
bool check( int ans ) {
int sum = 0;
for( int i = 1;i <= n;i ++ ) {
for( int j = 0;j <= w;j ++ )
if( ( ans | (a[i] >> j) ) == ans ) { sum += ( 1ll << w - j ); break; }
if( sum >= (1ll << w) ) return 1;
}
return 0;
}
int ans = ( 1ll << w ) - 1;
for( int i = w - 1;~ i;i -- )
if( check( ans ^ (1ll << i) ) ) ans ^= (1ll << i);
printf( "%lld\n", ans );
case 16~17
,特殊情况 A A A。
显然只需要尽可能让最高位 1 1 1 的二进制位最低。
那么每次从序列中选两个最大值,如果相同就合并右移一位,否则直接扔掉最大值。
priority_queue < int > q;
for( int i = 1;i <= n;i ++ ) q.push( a[i] );
while( q.size() > 1 ) {
int x = q.top(); q.pop();
int y = q.top(); q.pop();
if( x == y ) q.push( x >> 1 );
else q.push( y );
}
printf( "%lld\n", q.top() );
case 1~20
其实 case 13~15
已经非常接近正解了。
事实上,从高位到低位按位考虑的时候,每个 i i i 的 c i c_i ci 并不需要从头开始枚举。
因为为了满足前面更高位的一些限制的时候,有的 i i i 就已经要求右移一定位数了。
那么此时我们完全可以直接从之前右移的位数继续累加考虑。
这样每个 i i i 的 c i c_i ci 就只变化了一个 1 ∼ ω 1\sim \omega 1∼ω 的范围。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
int T, n, w;
int a[maxn], temp[maxn], g[maxn];
signed main() {
scanf( "%lld", &T );
while( T -- ) {
scanf( "%lld %lld", &n, &w );
for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] );
memset( g, 0, sizeof( g ) );
int ans = (1ll << w) - 1;
for( int k = w - 1;~ k;k -- ) {
int now = ans ^ (1ll << k), sum = 0;
for( int i = 1;i <= n;i ++ ) {
temp[i] = g[i];
while( ( a[i] >> g[i] | now ) ^ now ) g[i] ++;
sum += 1ll << w - g[i];
if( sum >= (1ll << w) ) sum = (1ll << w); //一直加可能炸long long
}
if( sum >= (1ll << w) ) ans = now;
else memcpy( g, temp, sizeof( g ) );
}
printf( "%lld\n", ans );
}
return 0;
}