URAL2049 Chemistry

Link
先判断(k=n),很显然有解的充要条件是(n=2^t(tinmathbb N)),构造用二进制合并就行了。
然后若(2 mid n),那么此时一定有解,依旧可以类似地二进制分组构造。
(2|n),那么有解的充要条件是(k e 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
",(int)ans.size());for(pi x:ans)printf("%d %d
",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();
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12614868.html