CF1408F Two Different
Pro:
Sol:
这种构造题没有什么捷径
就是多手玩,多总结
考虑n=2^k的时候
我们可以用一个简单的分治来构造出合法解
可以很容易的得到2^k个相同的数字
可以总结出这样一个性质
\(2^a\)个\(x\)和\(2^a\)个\(y\)可以合并成\(2^{a+1}\)个\(xy\)
考虑n=2^k+c的时候怎么做
把c拆解为\(2^a + 2^b + 2^c +....\)
这个时候我们仍按照刚才分治合并的策略来执行
但这个时候我们会发现由于a b c不连续
所以向上合并的时候会缺少元素
怎么办呢?
不如直接从2^k借一些过来!
我们可以事先预处理是的2^k个元素合并为同一个数字
当处理c不够用是直接从2^k借就可以了!
又由于c<2k所以,2k一定是够用的!
用n=13举个例子
13=8+5
5=1+4
预处理为这种形式
a bbbb
向2^k借一个1
aa bbbb
向2^k借一个2
aaaa bbbb
合并
aaaaaaaa
#include<bits/stdc++.h>
#define N 550000
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
inline int read()
{
char ch=0;
int x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
return x*flag;
}
const int mo=998244353;
int tot;
struct node{int x,y;}p[N];
#define mid ((l+r)>>1)
#define lb(x) ((+x)&(-x))
void solve(int l,int r)
{
if(l==r)return;
solve(l,mid);
solve(mid+1,r);
for(int i=l,j=mid+1;i<=mid&&j<=r;i++,j++)p[++tot]={i,j};
}
int F(int x,int y)
{
return ((x^y^(x+y))+(x+y)+(x/y)+(y/x))%mo;
}
int a[N],f[N],g[N],nw[N];
int main()
{
int n=read(),m=n,cnt=0;
while(m)a[++cnt]=lb(m),m-=lb(m);
for(int i=1,k=1;i<=cnt;i++)solve(k,k+a[i]-1),k+=a[i];
if(cnt<=2)
{
printf("%d\n",tot);
for(int i=1;i<=tot;i++)printf("%d %d\n",p[i].x,p[i].y);
return 0;
}
for(int i=1;i<=a[1];i++)nw[i]=i;
for(int i=1;i<=n-a[1]-a[cnt];i++)f[i]=a[1]+i;
for(int i=1;i<=a[cnt];i++)g[i]=n-a[cnt]+i;
for(int i=1,t=0,k=0,len=a[1];i<=cnt-2;i++)
{
while(len<a[i+1])
{
int tmp=len;
for(int j=1;j<=tmp;j++)p[++tot]={nw[j],g[++t]},nw[++len]=g[t];
}
int tmp=len;
for(int j=1;j<=tmp;j++)p[++tot]={nw[j],f[++k]},nw[++len]=f[k];
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++)printf("%d %d\n",p[i].x,p[i].y);
/*
for(int i=1;i<=n;i++)a[i]=i;
for(int i=1;i<=tot;i++)
{
int x=p[i].x,y=p[i].y;
a[x]=a[y]=F(a[x],a[y]);
}
for(int i=1;i<=n;i++)cout<<a[i]<<endl;
*/
return 0;
}