P10026题解
P10026 「HCOI-R1」哀之变化
题解
这么有趣的分类讨论题肯定要写一篇题解啦!
首先,感觉题目给出的两个操作很像二进制拆分,所以我们考虑倒着把 \(n\) 变成 \(1\),每次加一或除以二。
我们先把最少操作数给求出来,记为 \(m\)。直观来说,肯定是除以二的收敛速度比较快(废话),所以是偶数就除以二,是奇数就加一,模拟一下就可以得到最小操作次数,显然的,是 \(O(\log n)\) 级别所以不会超时。
然后就要靠我们惊人的注意力了:注意到 \(1\times 2-1=1\),所以一定有一个长为 \(2\) 的周期。又注意到若 \(n>1\),则第一步只能是 \(1\times 2=2\),而 \(2\times 2-1-1=2\),因此又有一个长为 \(3\) 的周期。结合一下可以得到:
-
\(n=0\):只要 \(k\ge1\) 即有解,因为 \(0\times 2=0\)。
-
\(n=1\):只要 \(k\not=1 \operatorname{and} k\not=3\) 即有解。
-
\(n>1\):\(k\ge m\operatorname{and}k\not=m+1\) 则必定有解,\(k<m\) 则必定无解。
但是 \(k=m+1\) 是不能判定有无解的。比如 \(n=5\),最小操作为 \((1\times 2\times 2-1)\times 2-1=5\),需 \(m=5\) 次操作,然而存在 \(1\times 2\times 2\times 2-1-1-1=5\),共 \(6\) 次操作;而对于 \(n=4\),有 \(1\times 2\times 2=4\),故 \(m=2\),但是可以发现没有操作次数为 \(3\) 的序列,于是我们就挂了。
为什么会出现这种情况呢?其实是因为如果我们的操作序列中出现了先减 \(1\) 再 \(\times 2\) 这种东西,那我们可以拆括号 \((x-1)\times 2=(x\times 2-1-1)\),就完成了从 \(2\) 次操作扩展到 \(3\) 次操作。
于是,满足 \(k=m+1\) 无解的所有数都应该满足:使其取到 \(m\) 的操作序列应为:\(1\times 2\times 2\times\dots\times 2-1-1-\dots-1\)。不过又由于我们在求 \(m\) 时只有在奇数时才会使用第一种减一操作,因此最后的那一连串 \(-1\) 实际最多只会有一项。
再细想一下,就不难发现所有 \(2\) 的幂和 \(2\) 的幂减一的 \(k=m+1\) 是无解的,而其余均有解。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd() {
int s=0,m=0;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')m=1;ch=getchar();}
while( isdigit(ch)) s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return m?-s:s;
}
int t,n,k,s;
bool check(int x) {return (1ll<<(int)log2(x))==x; }
signed main() {
cin>>t;
while(t--) {
k=rd(),n=rd(),s=0;
if(n==0) {puts(k!=0?"Yes":"No");continue;}
if(n==1) {puts(k!=1&&k!=3?"Yes":"No");continue;}
for(int u=n;u!=1;s++)
if(u%2) u++;
else u/=2;
if(k<s) puts("No");
else if(k!=s+1) puts("Yes");
else if(!check(n+(n%2))) puts("Yes");
else puts("No");
}
return 0;
}
本文来自博客园,作者:operator,转载请注明原文链接:https://www.cnblogs.com/operator-/p/17974782 ♪(^∀^●)ノシ