18.9.26 考试总结
这道题我考试的时候没想出来 冷静分析一波
设一开始小$K$ 又$n$块芝麻 总共有$sum$块 那么小$X$有$sum - n$块
那么每次变化有两种情况
1.$n$更小 那么$n$变成$2n$
2.$n$更大 那么$sum - n$变成$2 * (sum - n)$ 共有$sum$块 那么$n$变为$sum - 2 * (sum - n) = 2n - sum$
这两个最终状态的$n$在模$sum$意义下是一样的 相当于没有变化..
所以就快速幂走一波就可以了
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll sum,n,m,k; ll fast_pow(ll a, ll b, ll mod) { ll ans = 1; for(;b;b >>= 1,a = a * a % mod) if(b & 1) ans = ans * a % mod; return ans; } int main( ) { freopen("sesame.in","r",stdin); freopen("sesame.out","w",stdout); scanf("%lld%lld%lld",& n,& m,& k); sum = n + m; ll del = fast_pow(2, k, sum); n = n * del % sum; printf("%lld\n",min(sum - n, n)); }
然后这道题我考试的时候是 想!出!来!正!解!了!的!
然后对拍老不过 死的明明北北QAQAQAQAQ
考试完了两分钟就改出来了!!!少了一句特判!!!掉了50分!!!!
这道题题目是 $a_{x} = q\cdot a_{y} + K$ 那么也就是说$a_{x} - K$是$a_{y}$的倍数 若一对数满足这个条件 则这对数是坏对
那么我们扫一遍 对于每个位置$i$找出以$i$作为区间结尾的区间个数就可以了
所以考虑如何在前面的数中找出一对坏对最后出现的位置 也就是$$的倍数 也就是从那个位置之后的位置都可以作为合法区间的开头
然后看到数的范围比较小 所以就考虑开桶 $pos[i]$表示$i$的倍数最后出现的位置
对于每一个位置的$a[i] - K$ 处理出他的所有因子 更新这些$pos$即可
这时候少了一些特判 如果一个数小于等于$K$那么模它之后不可能等于$K$ 也就是说它模任何数都合法 就不去更新现在的最后不合法位置
另外如果这个数等于$K$ 那么之后小于等于他的数刚才已经说了就不管了 大于他的数则一定与他形成坏对 所以还要记录他的位置 遇到大于$K$的位置时更新最后不合法的位置
然后搞一搞就可以了
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n,MOD,pos[N],a[N]; long long ans = 0; void Init( ) { scanf("%d%d",& n,& MOD); for(int i = 1;i <= n;i ++) scanf("%d",& a[i]); } void work(int x, int p) { for(int i = 1;i * i <= x;i ++) { if(x % i == 0) pos[i] = p, pos[x / i] = p; } } void Solve( ) { int las = 0,tag = 0; for(int i = 1;i <= n;i ++) { int pre = pos[a[i]]; if(a[i] > MOD) las = max(max(las, pre), tag); ans += 1ll * max(i - las, 0); if(a[i] - MOD > 0) work(a[i] - MOD, i); if(a[i] == MOD) tag = i; } printf("%lld\n", ans); } int main( ) { freopen("drink.in","r",stdin); freopen("drink.out","w",stdout); Init( ); Solve( ); }
这道题是是一道dp题 其实还是挺难的 题解写在注释了
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e3 + 5; const int M = 1e6 + 5; int n,m,l[M]; ll MOD,p[M],fac[M],d[2][N],f[N][N]; void Init( ) { scanf("%d%d%lld",& n,& m,& MOD); p[0] = 1; fac[0] = 1; for(int i = 1;i <= m;i ++) p[i] = p[i - 1] * (m - i + 1) % MOD;//P(m,i) for(int i = 1;i <= m;i ++) fac[i] = fac[i - 1] * i % MOD;//阶乘 for(int i = 1;i <= n;i ++) scanf("%d",& l[i]); f[1][1] = 1; for(int i = 2;i <= 5000;i ++) for(int j = 1;j <= i;j ++)//每个点只有两种选择 选新颜色或者旧颜色 f[i][j] = ((f[i - 1][j - 1] + f[i - 1][j] * (j - 1) % MOD) % MOD + MOD) % MOD;//在i个格子里选择j种颜色的合法方案 } void Solve( ) { int now = 0; for(int i = 1;i <= l[1];i ++) d[now][i] = p[i] * f[l[1]][i] % MOD;//第1层选择i种颜色的方案数 for(int i = 2;i <= n;i ++) { ll sum = 0; now ^= 1; for(int j = 1;j <= l[i - 1];j ++) sum = (sum + d[now ^ 1][j]) % MOD; for(int j = 1;j <= l[i];j ++) { d[now][j] = (p[j] * f[l[i]][j] % MOD) * sum % MOD; if(j <= l[i - 1]) d[now][j] = (d[now][j] - (d[now ^ 1][j] * f[l[i]][j] % MOD * fac[j] % MOD) + MOD) % MOD; }//除去不合法的方案 因为上一层的颜色是确定的 所以乘阶乘而不是P } ll ans = 0; for(int i = 1;i <= l[n];i ++) ans = (ans + d[now][i]) % MOD; printf("%lld\n",ans); } int main( ) { freopen("kalanchoe.in","r",stdin); freopen("kalanchoe.out","w",stdout); Init( ); Solve( ); }