ARC182B |{floor(A_i/2^k)}| 題解
題目大意
定義一個長度為 \(N\) 的序列 \(A\) 的分數為 能被表示成 \(\lfloor {A_i\over2^k}\rfloor\) 的數的個數,其中 \(i=1,2,\dots,N\),\(k\) 為任意自然數。
給定 \(N,K\),求長度為 \(N\) 且元素大小都在 \(2^K-1\) 內的所有序列的分數的最大值,輸出任意擁有最大分數的序列。
Solve
除以 \(k\) 下取整相當於二進位制下右移 \(k\) 位,所以按位考慮。
顯然有效位數越多越好,因為有效位數多的右移 \(k\) 位得到的數也更多。比如 \((101010)_2\) 右移得到的數是包含 \((01010)_2\) 能得到的數的。
所以序列中的數二進位制下第 \(K-1\) 位一定是 \(1\),接下來我們考慮怎麼構造能使右移得到的不同的數更多。這裡有一種構造方法:
1 0 0 0 ...
1 1 0 0 ...
1 0 1 0 ...
1 1 1 0 ...
1 0 0 1 ...
1 1 0 1 ...
1 0 1 1 ...
1 0 1 1 ...
1 0 0 0 ...
1 1 0 0 ...
1 0 1 0 ...
1 1 1 0 ...
...
即二進位制位儘量交錯著填,保證每一種情況都被考慮,比如第 \(K-4\) 位上填 \(0\) 的數中,第 \(K-2,K-3\) 位上填 \(1\) 或 \(0\) 的所有 \(4\) 種情況都被考慮到。
如此,我們能保證右移任意 \(k\) 位得到的數的個數都是卡滿的。
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
short f=1;
int x=0;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
const int N=1e5+10;
int T,n,m,a[N];
signed main()
{
T=read();
while(T--)
{
n=read();m=read();
for(int i=1;i<=n;i=-~i) a[i]=1<<m-1;
for(int i=m-2;i>=0;i--)
{
bool f=0;
for(int j=1;j<=n;j+=(1<<m-2-i),f^=1)
for(int k=j;k<=min(j+(1<<m-2-i)-1,n);k=-~k)
a[k]+=f<<i;
}
for(int i=1;i<=n;i=-~i) printf("%d ",a[i]);
putchar('\n');
}
return 0;
}