codeforces1027 Build an Array题解
题目链接:
https://codeforces.com/contest/2114/problem/G
思路:
首先观察的重要的一个性质就是一个数最少需要一次,最多则与他能被\(2\)整除多少次有关。
比方说一个数可以分解成\(2^n*x\)的形式,那这个就可以一直放\(x\)实现最多的\(2^n\)次。
并且这个次数从\(1\)到\(2^n\)每个数都能取到,原因就是你可以把他看作一颗二叉树,每次\(÷2\)就像二叉树分出两个儿子,然后这样会使得叶子节点个数\(+1\),所以都能取到。
所以只要\(n\leq k \leq\)(每个数的次数和)。因为题目条件\(n\leq k\),所以只要\(k \leq\)(每个数的次数和)就可以,否则就不行。
但你看样例直接统计每个数的最大次数然后加起来这样是不对的,比如说数组是\(2,1,4\)则会发现如果一直插\(1\),中间的\(1\)一定会合并,从此可以看出,如果我现在构造某个数时,旁边有和我能分解成同一个结构的\(2^i*x\)的,且比我小我就会损失次数,我最好要避免这种情况,比如\(12,3\)我先弄出\(12\)优于先弄出\(3\)。
于是首先弄出谁是至关重要的,因为这会决定构造每个数时自己的邻居是谁,第一个数确定每个数的邻居也就确定了。\(A,B,C,D\),用箭头指向代表邻居则有
\(A\leftarrow B \leftarrow C \leftarrow D\),\(A\rightarrow B \leftarrow C \leftarrow D\),
\(A\rightarrow B \rightarrow C \leftarrow D\),\(A\rightarrow B \rightarrow C \rightarrow D\)。
这\(4\)种情况。
然后我现在在构造\(2^a*X\),邻居是\(2^b*X\),我就要先生成一个\(2^{b+1}*X\)来隔离这个邻居,因为放任何比这小的都无法构造,这时候我构造这个数就只能产生\(2^a-2^{b+1}+1\)的次数了。然后我就要枚举谁第一个生成。但直接枚举会超时,然后观察发现,每种情况都是两段两种方向的箭头拼在一起,这个可以前后缀预处理。
代码里我用\(dp\)维护产生该数的最多次数,\(who\)维护除去\(2^n\)的部分我是谁,\(pre\)是我的邻居是我左边的数,\(suf\)是邻居是我右边的数。处理好后,求最大次数则用$ ans=max(ans,dp[i]+suf[1]-suf[i]+pre[n]-pre[i])$求出最多次数即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010;
int v[N];
int dp[N];
int who[N];
void solve(){
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>v[i];
}
for(int i=1;i<=n;i++){
int x=v[i];dp[i]=1;
while(x%2==0){
x/=2;
dp[i]*=2;
}
who[i]=x;
}who[n+1]=0;
vector<int>pre(n+2),suf(n+2);
int ans=0;
for(int i=1;i<=n;i++){
if(who[i]==who[i-1]&&v[i]>v[i-1]){
pre[i]=pre[i-1]+dp[i]-dp[i-1]*2+1;
}else pre[i]=pre[i-1]+dp[i];
}
for(int i=n;i>=1;i--){
if(who[i+1]==who[i]&&v[i]>v[i+1]){
suf[i]=suf[i+1]+dp[i]-dp[i+1]*2+1;
}else suf[i]=suf[i+1]+dp[i];
}
for(int i=1;i<=n;i++){
ans=max(ans,dp[i]+suf[1]-suf[i]+pre[n]-pre[i]);
}
if(ans>=k){
cout<<"YES"<<endl;
}else cout<<"NO"<<endl;
}
signed main(){
#ifdef ONLINE_JUDGE
#else
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
ios::sync_with_stdio(false);cin.tie(0);
int T;cin>>T;
while(T--){
solve();
}
}

浙公网安备 33010602011771号