URAL2049 Chemistry
Link
先判断\(k=n\),很显然有解的充要条件是\(n=2^t(t\in\mathbb N)\),构造用二进制合并就行了。
然后若\(2\nmid n\),那么此时一定有解,依旧可以类似地二进制分组构造。
若\(2|n\),那么有解的充要条件是\(k\ne n-1\),构造方法依旧是二进制分组。
#include<cstdio>
#include<vector>
#include<utility>
#define fi first
#define se second
using pi=std::pair<int,int>;
std::vector<pi>ans,tmp;
void out(){printf("%d\n",(int)ans.size());for(pi x:ans)printf("%d %d\n",x.fi,x.se);}
void merge(pi&a,pi&b){ans.push_back({a.fi,b.fi}),a.se-=b.se,b.se*=2;}
int main()
{
int n,k,f=0;scanf("%d%d",&n,&k);
if(k==1) return puts("0"),0;
if(n==k&&__builtin_popcount(n)^1) return puts("-1"),0;
if(k==n-1&&~n&1) return puts("-1"),0;
if(n==k)
{
for(int i=1;i<n;i+=i) for(int j=1;j<=n;j+=i+i) ans.push_back({j+i,j});
return out(),0;
}
if(k&1) f=1,++k;
for(int i=17,p=1;~i;--i)
{
if(!((k+1)>>i&1)) continue;
tmp.push_back({p,1<<i});
for(int j=1;j<1<<i;j+=j) for(int k=0;k<1<<i;k+=j+j) ans.push_back({p+j+k,p+k});
p+=1<<i;
}
while(tmp.size()>2) for(merge(tmp.front(),tmp.back());tmp.back().se==prev(prev(tmp.end()))->se;tmp.pop_back()) merge(tmp.back(),*prev(prev(tmp.end())));
for(int p;tmp.front().se^k;) p=tmp[0].se<tmp[1].se,merge(tmp[p],tmp[!p]);
if(f) ans.push_back({1,tmp[1].fi});
out();
}