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();
}
posted @ 2020-04-01 19:02  Shiina_Mashiro  阅读(182)  评论(1编辑  收藏  举报