莎比题.
一.题目链接
https://codeforces.com/gym/102821/problem/J
二.题意
有三种卡片,上面分别写着1,2,3.每次可以选一张卡片,然后向前走.起点为0.
走法如下:如果这种牌之前没用过,上面写着+x就向前走x步;否则,向前走x+3步(x为上次使用这张牌走了几步).
问:走到第K个格子,(1)最少需要几步 (2)有多少种走法?
三.算法分析
抽象出来所有的卡片为3行
第几次选择 走了几步? | 1 | 2 | 3 | 4 | 101 | |
---|---|---|---|---|---|---|
卡片1( | 1 | 1+3 | 1+3*2 | 1+3*3 | … | 1+3*100 |
卡片2 | 2 | 2+3 | 2+3*2 | 2+3*3 | … | 2+3*100 |
卡片3 | 3 | 3+3 | 3+3*2 | 3+3*3 | … | 3+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;
}
五.总结
- 预处理阶乘及其逆元.
- 注意有longlong的每一个地方都查找一下,刚开始因为最后的输出mincnt没有改成lld一直哇.