18.9.18 考试总结
Posted rubenisveryhandsome
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18.9.18 考试总结相关的知识,希望对你有一定的参考价值。
状态不好怎么办呢
今天考试我服了 第一题很简单但是我没想出来 幸好第二题数据比较水...我垃圾暴力都水过了
这道题倒着搞 正着搞其实是一样的
因为要保证最后的珠子递增 所以就优先保证最后一个珠子放在空着的最后一个位置 其他的珠子就乱放就可以了
每次空着的位置减一下就可以了
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5 * 1e5 + 5; const ll MOD = 998244353; ll ans,fac[N],rfac[N]; int n,num[N],sum; ll fast_pow(ll a,ll b) { ll ans = 1; for(;b;b >>= 1,a = a * a % MOD) if(b & 1) ans = ans * a % MOD; return ans; } ll reverse(ll a) { return fast_pow(a,MOD - 2); } ll C(int i, int j) { return fac[i] * rfac[i - j] % MOD * rfac[j] % MOD; } void Init( ) { scanf("%d",& n); for(int i = 1;i <= n;i ++) { scanf("%d",& num[i]); sum += num[i]; } fac[0] = 1,rfac[0] = 1; for(int i = 1;i <= 500000;i ++) { fac[i] = fac[i - 1] * i % MOD; rfac[i] = reverse(fac[i]); } } void Solve( ) { ans = 1; for(int i = n;i >= 1;i --) { ans = (ans * C(sum - 1,num[i] - 1)) % MOD; sum -= num[i]; } printf("%lld",ans); } int main( ) { freopen("qiang.in","r",stdin); freopen("qiang.out","w",stdout); Init( ); Solve( ); }
因为这个每一行的转移是一样的 所以和轮廓线dp一样的思路
然后使用矩阵快速幂加速转移 然后因为我一直都不是很会矩阵快速幂...
先高高冯dalao的代码把
代码
#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); } }
这道题真的很神奇了
我感觉我至今还没有完全理解
先处理出一个数组表示从$a$到$b$需要多少次操作 如果某段区间内次数是单调递减的 说明这段区间的操作次数为最大的那个
处理出一个值表示将上一个位置的区间延展到现在的位置需要的代价 也是重新开几个次数的代价
因为我们要付出这个代价 就将当前区间的次数搞成4了 或者是当前的位置新开的代价
而我们每次选择最少钱的方案搞 所以如果小的在前面 它就相当于他那个位置补齐 否则是新加的 都能延展到这个位置
然后就每次选择一个最少的方案搞就可以了 (其实我也不是很理解 当以后更厉害了再来看)
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n,T,ans,a[N],b[N],c[N],s[5]; bool vis[N]; void Solve( ) { scanf("%d",& T); while(T --) { scanf("%d",& n); ans = 0; for(int i = 1;i <= n;i ++) scanf("%d",& a[i]); for(int i = 1;i <= n;i ++) scanf("%d",& b[i]); for(int i = 1;i <= n;i ++) { if(b[i] == a[i]) c[i] = 0; else if(b[i] > a[i]) c[i] = b[i] - a[i]; else c[i] = b[i] + 4 - a[i]; } memset(s,0,sizeof(s)); ans = c[1]; for(int i = 2;i <= n;i ++) { s[((c[i] - c[i - 1]) % 4 + 4) % 4] ++; if(c[i] <= c[i - 1]) continue; int j; for(j = 1;s[j] == 0;j ++); s[j] --; ans += j; } printf("%d ",ans); } } int main( ) { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); Solve( ); }
以上是关于18.9.18 考试总结的主要内容,如果未能解决你的问题,请参考以下文章