C. 三人成行
- 计数问题的核心是不重不漏。本题中,在同一个序列中合法的(x,y,z,u)可能在多个位置出现,为了避免重复,我们取最左边的位置作为代表,一旦符合题意就把它计入答案并终止后续的状态转移
- 纠结局部会把自己弄晕的!拒绝晕倒,能不能从整体考虑,不记录当前的匹配情况,直接判定当前序列是否已经满足条件?完全可以!而且,一步步匹配一点都不美
- 另一方面,你已经观察到使序列合法的子区间状态是有限的,进一步,这些合法状态完全可以通过压缩后缀和表示。而且,题目告诉你的条件是\(X+Y+Z \leq 17\),何必弱化为\(X,Y,Z \leq 15\)呢
#include <bits/stdc++.h>
using namespace std;
const int mod=1000000007;
long long f[45][1<<18],ans;
int n,X,Y,Z;
bool fx,fy,fz;
int i,l,s,t;
int power(int n,int p)
{if(p==0){return 1;}long long tmp=power(n,p/2);if(p%2==1){return tmp*tmp%mod*n%mod;}return tmp*tmp%mod;
}
void dfs(int n1)
{if(n1==-1){if(fx&&fy&&fz){ans=(ans+f[i][s]*power(10,n-i-1)%mod)%mod;}else{(f[i+1][t]+=f[i][s])%=mod;}}else{for(int i=0;i<2;i++){if(n1+l<Z){t=t+i*(1<<(n1+l));}if(i==1){if(n1+l==X){fx=true;}if(n1+l==Y){fy=true;}if(n1+l==Z){fz=true;}}s<<=1;s+=i;dfs(n1-1);s-=i;s>>=1;if(i==1){if(n1+l==X){fx=false;}if(n1+l==Y){fy=false;}if(n1+l==Z){fz=false;}}if(n1+l<Z){t=t-i*(1<<(n1+l));}}}
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cin>>n>>X>>Y>>Z;Y=X+Y;Z=Y+Z;f[0][1]=1;t=1;for(i=0;i<n;i++){for(l=1;l<=10;l++){dfs(Z-1);}}cout<<ans<<endl;return 0;
}