又开始恐怖的数论专题了
题解:
两种理解:补集转换
f[i]表示填了前i个的合法方案, tot[i]表示填了前i个的所有方案, p[i]填了前i个的不合法方案;
这道题相当于填n个数,使他们抑或和=0(太神奇了(⊙o⊙))
如果前i-1个不合法,我们可以填一个数使他合法,那么就是p[i-1], 但是有些数填进去就会重复;所有我们要除去所有不合法的方案:
如果是重复的,那么我们把重复的两个剃掉,剩下的就一定是合法的,就是f[i-2],其中可以剃掉的数可以选(2^M - 1 - (i-2))个, 但是在踢的时候也会出现重复的情况, 比如我们要踢aa可以出现在第一个位置,又可以出现在最后一个位置, 所以还要除以i;
合法的方案就等于所有方案-不合法的方案;
p[i] = f[i-1]- f[i-2]*(2^M - 1 - (i-2))/i; f[i] = tot[i] - p[i];
直接dp:dp[i]表示处理了前i个的合法方案,那么前i-1个数的填发方案为(2^M - 1)(2^M - 2)(2^M - 3)…… (2^M - i + 1) ,所以讨论第i位的填法, 显然合法的方案很难求,正难则反;
第一种,前面是合法的,我们填不动了,那么i只能填空集,这样有dp[i-1]种;
第二种,我们填重复了,填重复的数有(2^M - 1 - (i-2))个,这个重复的数可能在(i-1)个位置;
dp[i] = (2^M - 1)(2^M - 2)(2^M - 3)…… (2^M - i + 1) - f[i-1]*(i-1)*(2^M-1 - (i-1));
#include#define N_MAX 1000000#define lld "%llu"typedef unsigned long long lnt;typedef unsigned unt;const unt P = 100000007;inline lnt moc(lnt a) { return a < P ? a : a - P; }inline lnt mod(lnt a) { return a < P ? a : a % P; }inline lnt qow(lnt a, unt k){ static lnt w; for (w = 1; k; a = mod(a * a), k >>= 1) if (k & 1) w = mod(w * a); return w;}lnt c, e, f[5];unt m, n, i, j;int main(){ scanf("%u %u", &m, &n); e = c = qow(2, m) - 1; f[0] = 1, f[1] = 0, f[4] = 1; for (i = 2, j = 1; i <= n; ++i, ++j &= 3) { f[(j + 1) & 3] = moc(e - mod(f[j] + mod(f[(j - 1) & 3] * c) * (i - 1)) + P); e = mod(e * (c = moc(c - 1 + P))); f[4] = mod(f[4] * i); } printf(lld "\n", mod(f[j] * qow(f[4], P - 2))); return 0;}
第二题:dp
对于一个字符,一是和前面连起来,有dp[i-1]种连法,二是不要他,有dp[i-1],三是自己单独为一个,1;
对于重复的:我们可以记录a[i]上一次出现的位置,那么这次的a[i]和上次a[i]前面连起来肯定重复了,那么就是2*dp[i-1] - dp[last[a[i] - 1];
所以 :
dp[i] = 2 * dp[i-1] + 1 (if a[i] hasn't appeared)
dp[i] = 2 * dp[i-1] - dp[last[ai] - 1]
#include#define ll long longusing namespace std;const int M = 1e6 + 10;const ll p = 1e9 +7;ll dp[M];int a[M], lst[M];int main(){ freopen("B.in", "r", stdin); freopen("B.out", "w", stdout); int n; scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++){ if(!lst[a[i]]) dp[i] = (2*dp[i-1] + 1) % p; else dp[i] = ( (2*dp[i-1] - dp[lst[a[i]] - 1]) % p + p ) % p; lst[a[i]] = i; } printf("%I64d\n", dp[n]);}
第三题:显然每个真菌都是独立的,只需计算一个的概率,最后? 次方即可。
记?[?] 表示一个真菌及其后代在? 天内全死亡的概率,则
?[?] =Σ︁(i=0--m-1) ?? * ?[? − 1]^i;
注意转移时不用快速幂,只需每次多乘一个?[? − 1],这样时间复杂度为(lm)
#includeusing namespace std;#define ll long longconst int M = 1005; ll f[M], sum, p[M];const ll mod = 1e9+7;int main(){ freopen("C.in","r",stdin); freopen("C.out","w",stdout); int n, m, L; scanf("%d%d%d", &m, &n, &L); for(int i = 0; i < m; i++)scanf("%I64d", &p[i]); //f[0] = p[0]; for(int k = 1; k <= L; k++){ ll bas = 1; for(int j = 0; j < m; j++){ f[k] = (f[k] + p[j] * bas) % mod; bas = (bas * f[k-1]) % mod; } } ll ans = 1; for(int i = 1; i <= n; i++) ans = (ans * f[L]) % mod; printf("%I64d\n", ans);}