2019 Sichuan Province Programming Contest J. Jump on Axis 题解

莎比题.

一.题目链接

https://codeforces.com/gym/102821/problem/J

二.题意

有三种卡片,上面分别写着1,2,3.每次可以选一张卡片,然后向前走.起点为0.

走法如下:如果这种牌之前没用过,上面写着+x就向前走x步;否则,向前走x+3步(x为上次使用这张牌走了几步).

问:走到第K个格子,(1)最少需要几步 (2)有多少种走法?

三.算法分析

抽象出来所有的卡片为3行

第几次选择 走了几步?1234101
卡片1(11+31+3*21+3*31+3*100
卡片222+32+3*22+3*32+3*100
卡片333+33+3*23+3*33+3*100

假设卡片1,卡片2,卡片3分别使用了i,j,k张,表上的前缀和分别为i1,j1,k1.

则i1+j1+k1=K.用了i+j+k步(可更新最小步数),有C(i+j+k,i)*C(j+k,j)=(i+j+k)!/i!/j!/k!种(更新种类数ans).

四.代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define clr(a, b) memset(a, b, sizeof(a))
#define rep(i,a,b) for (register int i=(a); i<=(b); i++)
#define per(i,a,b) for (register int i=(a); i>=(b); i--)
#define debug(x) std::cerr << #x << '=' << x << std::endl
using namespace std;
typedef long double ld;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> Vi;
template<class T> inline void read(T &x) {
	x=0;
	char c=getchar();
	int f=1;
	while (!isdigit(c)) {
		if (c=='-') f=-1;
		c=getchar();
	}
	while (isdigit(c)) {
		x=x*10+c-'0';
		c=getchar();
	}
	x*=f;
}
template<class T> T gcd(T a, T b) {
	return !b?a:gcd(b,a%b);
}
template<class T> inline void umin(T &x, T y) {
	x=x<y?x:y;
}
template<class T> inline void umax(T &x, T y) {
	x=x>y?x:y;
}

const double EPS = 1e-8;
const double PI = acos(-1.0);


/*-------------------------------end-----------------------------------------*/
const int maxn=1e4+200;
const ll mmm=1e9+7;
ll f[maxn],g[maxn],ans,K,p=1e9+7,mincnt;

ll qpow(long long a, ll b) {
	ll ans = 1;
	a = (a % p + p) % p;
	for (; b; b >>= 1) {
		if (b & 1) ans = (a * ans) % p;
		a = (a * a) % p;
	}
	return ans%mmm;
}

void init() {
	//f=i! g=1/i!
	f[0]=1;
	rep(i,1,1e4) f[i]=f[i-1]*i%mmm;
	rep(i,0,1e4) g[i]=qpow(f[i],mmm-2);
}

void work() {
	ll i1,j1,k1,dlt1,k;
	for (int i=0;; i++) {
		i1=(ll)i+3*i*(i-1)/2;
		if (i1>K) return;
		for (int j=0;;j++) {
			j1=(ll)2*j+3*j*(j-1)/2;
			if (i1+j1>K) break;
			k1=K-i1-j1;
			if (9+24*k1<0) continue;
			dlt1=(ll)sqrt(9+24*k1);
			k=(-3+dlt1)/6;
			if (3*k+3*k*(k-1)/2!=k1) continue;
			umin(mincnt,(ll)i+j+k);
			ans=(ans+f[i+j+k]*g[i]%mmm*g[j]%mmm*g[k]%mmm)%mmm; 
		}
	}
	return;
}

int main() {
	int T;
	cin>>T;
	init();
	rep(it,1,T) {
		cin>>K;
		mincnt=K,ans=0;
		work();
		printf("Case %d: %lld %lld\n",it,mincnt,ans);
	}
	return 0;
}

五.总结

  1. 预处理阶乘及其逆元.
  2. 注意有longlong的每一个地方都查找一下,刚开始因为最后的输出mincnt没有改成lld一直哇.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值