agc012_c Tautonym Puzzle (构造)

这题的构造技巧在于尽量简化操作,让贡献便于计算。

最简单的情况就是每一个数字只有两个了。

记一个长度为n的排列为P_n,我们考虑构造这样一个序列Q=P_n,1,2,...,n)。

容易发现,一个复读子序列TT要想出现在这里面,肯定是前一份TP_n里面,后一份T在(1,2,...,n)里面。

这就要求T本身是一个递增序列。

因此这样一个序列Q的贡献实质就等于P_n的上升子序列个数f(P_n)

这样问题就简单了(虽然暂时我们也并不知道n取多大)。

考虑怎么构造一个P_n凑出N的贡献。

由于存在以下递推式:

f(P_{n-1},n)=2f(P_{n-1})+1

f(n,P_{n-1})=f(P_{n-1})+1

所以我们可以根据当前N的奇偶性,递归地解决子问题,从而构造出P_n

然后我们在P_n末尾加上(1,2,...,n)就大功告成了。

显然不出两步N就会缩小一半,所以可以在2logN步以内构造完毕。

由于N<=1e12,所以n<=80,|Q|<=160,长度足够短了。

 

#include <cstdio>
#include <vector>
#define ll long long
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
ll n;
int i,k;
vector <int> a;
void solve(ll n,int &k)
{
	if (n==1) {a.push_back(1); k=1; return ;}
	if (n&1) {
		solve((n-1)/2,k);
		k++; a.push_back(k);
	}
	else {	
		solve(n-1,k);
		k++; a.insert(a.begin(),k);
	}
}
int main()
{
	scanf("%lld",&n);
	solve(n,k);
	rep(i,1,k) a.push_back(i);
	printf("%d\n",a.size());
	rep(i,0,a.size()-1) printf("%d ",a[i]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值