题意
对于一个(n+1)*(n+1)的矩阵,第一列和第一排为1,其他位置为1当且仅当上方和左方有一个1,其他为0;
对于100%的数据,n<=1e9
题解
稍微画了一下,感觉从图像看不大出来,就去打了一个表:
1,3,7,9,17,21,25,27,43,51,59,63,71,75,81;
这种东西一般就求差再看看,就会发现:2,4,2,8,4,4,2,16,8,8,4,8,4,4,2
这样还看不大出来,再分析数据对于30%的数据n=2k-1,发现这个范围的答案就是3k,于是直觉就分出了组
2
4,2
8,4,4,2
16,8,8,4,8,4,4,2
那么就比较明显了吧,下面一排前一半是上一排*2,后一半就是上一排。
那么就想到一种做法,先把n分成2k+n',就得到3k,剩下n'部分再递归处理,他是一个组的一部分,再这个组的前一半的部分就是上一个组的一半,在这个组后一半的部分直接递归。
注意记忆化就ok,可以用map记忆化,其实差不多,map还有查找速度,虽然数组只能只能开1e6。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=1000005; ll n; ll ans,f[35][maxn]; int get(int x){ int ret=-1; while(x){ x>>=1; ret++; } return ret; } ll qpow(ll a,ll b){ ll ret=1; while(b){ if(b&1) ret*=a; a*=a; b>>=1; } return ret; } ll dfs(int s,int k){//第s组,前k项 if(s==1) return 2; if(k<maxn&&f[s][k]) return f[s][k]; ll mid=(1<<(s-2)),ret; if(k<=mid) ret=dfs(s-1,k)<<1; else ret=dfs(s-1,k-mid)+(dfs(s-1,mid)<<1); if(k<maxn) f[s][k]=ret; return ret; } int main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%lld",&n); n++; int k=get(n); ans+=qpow(3,k); n-=(1<<k); if(!n) {printf("%lld",ans);return 0;} ans+=dfs(k+1,n); printf("%lld",ans); }
不过他们的规律好像和我不大一样,他们直接从答案入手,给出代码,具体原因之后再看。(code from ltw)
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<map> using namespace std; long long n; map < int,long long > mp; long long solve (long long x){ if (x==1) return 1; if (x==2) return 3; if (mp[x]) return mp[x]; return mp[x]=2*solve (ceil ((double)(x/2.0)))+solve (floor ((double(x/2.0)))); } int main(){ freopen ("c.in","r",stdin); freopen ("c.out","w",stdout); scanf ("%lld",&n); printf ("%lld",solve (n+1)); }
还有看图像找规律的,三个1组成一个小三角形,再三个又是一个更大的。
先取一个lowbit(就是分解成2k+n'),然后再怎么搞搞就OK了。(咱也不知道,咱也不敢问)
不过正解好像是数位DP?讲这个图形旋转45°就是杨辉三角?然后在利用Lucas定理?
话说今天数论都是打表搞的,这真是一个好方法。(掩饰自己不会数论)
顺便%%sxk dalao,AK