第一题:
插板法,多思考一下应该出得来;
#include<bits/stdc++.h> using namespace std; #define ll long long const ll mod = 998244353; const int M = 1000005; ll dp[M], fac[M], a[M], vfac[M], sum[M]; ll ksm(ll a, ll b){ ll ret=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ret=ret*a%mod; return ret; } inline ll ni(ll a){ return ksm(a, mod - 2); } int main(){ freopen("qiang.in","r",stdin); freopen("qiang.out","w",stdout); int n; scanf("%d", &n); fac[0] = vfac[0] = 1; dp[1] = 1; int x; for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); sum[i] = sum[i - 1] + a[i]; } for(int i = 1; i <= sum[n]; i++) fac[i] = fac[i - 1] * 1LL*i % mod, vfac[i] = ni(fac[i]); for(int i = 2; i <= n; i++){ dp[i] = dp[i - 1] * fac[sum[i] - 1] % mod * vfac[a[i] - 1] % mod * vfac[sum[i - 1]] % mod; } printf("%lld ",dp[n]); }
第二题:矩阵块速幂,又听zyy大佬讲了一遍,讲的太好了;
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = (1<<4) + 2; int n; ll mod; inline ll moc(int a){return a >= mod ? a - mod : a;} struct Maxtri{ ll w[M][M]; void unit(){ for(int i = 0; i < M; i++) for(int j = 0; j < M; j++) w[i][j] = (i == j); } void init(){ for(int i = 0; i < M; i++) for(int j = 0; j < M; j++) w[i][j] = 0; } void print(){ for(int i = 0; i < M; i++){ for(int j = 0; j < M; j++)printf("%d ", w[i][j]);puts(""); } } }tr; Maxtri operator *(const Maxtri &s, const Maxtri &t){ Maxtri a; for(int i = 0; i < M; i++) for(int j = 0; j < M; j++){ a.w[i][j] = 0; for(int k = 0; k < M; k++) a.w[i][j] = moc(a.w[i][j] + s.w[i][k] * t.w[k][j] % mod); } return a; } Maxtri ksm(Maxtri a, int b){ Maxtri ret; for(ret.unit(); b; b >>= 1, a=a*a) if(b & 1) ret = ret * a; return ret; } void dfs(int dep, int os, int ns){ if(!dep) { tr.w[ns][os] = 1; return ; } if((1<<(dep-1))&os){ dfs(dep - 1, os, ns); if(dep - 1 > 0 && ((1<<(dep-2))&os)) dfs(dep - 2, os, ns + (1<<(dep-1)) + (1<<(dep-2))); } else dfs(dep - 1, os, ns + (1<<(dep-1))); } int main(){ freopen("count.in","r",stdin); freopen("count.out","w",stdout); int n; for(int i = 0; i < (1<<4); i++) dfs(4, i, 0); while(scanf("%d%lld", &n, &mod) == 2 && n && mod){ Maxtri rec; rec.init(); rec.w[(1<<4)-1][0] = 1; Maxtri ans = ksm(tr, n); //ans.print(); ll res = ans.w[(1<<4)-1][(1<<4)-1]; printf("%lld ", res); } }
第三题:先AB差分得到操作次数的数字,维护一个单调不降的数组,那么操作次数一定是第一个数;
怎么维护单降序列:如果后面凸起来, 1.重开一个新的, 2.接在原来的线上,我们就需要额外操作;
额外操作:(a[i] - a[i - 1] + 4) % 4, 使得当前操作次数不小于上一次操作次数;(相当于把新的填平了);
不论合不合法都记录额外操作,当不合法时,选取之前最小的代价,使每一步最优;
eg: 差分后数组
#include<bits/stdc++.h> using namespace std; const int M = 100005; int a[M], b[M], s[15]; int main(){ freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); int T; scanf("%d", &T); while(T--){ memset(s, 0, sizeof(s)); int n; scanf("%d", &n); for(int i = 1; i <= n; i++)scanf("%d", &a[i]); for(int i = 1; i <= n; i++)scanf("%d", &b[i]), a[i] = (b[i] - a[i] + 4) % 4; int ans = a[1]; for(int i = 2; i <= n; i++){ s[(a[i] - a[i - 1] + 4) % 4]++; if(a[i - 1] >= a[i])continue; int j; for(j=1;!s[j];j++); s[j]--; ans += j; } printf("%d ",ans); } }