一个很重要的观察是初始的sick把$[0, n - 1]$划分成了若干的段,它们是互不干扰的。考虑小孩$i$,它左侧最近的感染者是$j$,右侧最近的感染者是$k$,$i$要么被$j$感染,要么被$k$感染。下面独立考虑每个段,如果开头或结尾的段,则只有一种感染方式,否则就有$2^{k-j-1}$种。最后只需要将每个段依次两两合并即可。考虑一个长度为$x$和一个长度为$y$的段,总共有$x+1$个空位,插入$y$个数,相当于$x+1$个大于等于0的变量求和等于$y$,方案数为$C(x+y, x)$。
constint N = 1e5 + 5; constint MOD = 1e9 + 7; int fac[N], ifac[N]; intPow(int a, int b){ int ans = 1; while (b) { if (b&1) ans = 1ll*ans*a%MOD; a = 1ll*a*a%MOD; b >>= 1; } return ans; } voidinit(int n){ fac[0] = 1; for (int i=1; i<=n; i++) fac[i] = 1ll*fac[i-1]*i%MOD; ifac[n] = Pow(fac[n], MOD-2); for (int i=n-1; i>=0; i--) ifac[i] = 1ll*ifac[i+1]*(i+1)%MOD; } intC(int a, int b){ if (a<b||b<0) return0; return1ll*fac[a]*ifac[b]%MOD*ifac[a-b]%MOD; } classSolution { public: intnumberOfSequence(int n, vector<int>& sick){ init(n + 1); vector<int> two(n + 1); two[0] = 1; for (int i = 1; i <= n; i++) two[i] = (two[i - 1] << 1) % MOD; int ans = 1; int pre = sick[0]; for (int i = 0; i + 1 < sick.size(); i++) { int cur = sick[i + 1] - sick[i] - 1; if (!cur) continue; ans = 1ll*ans*two[cur - 1]%MOD*C(cur + pre, pre)%MOD; pre += cur; } ans = 1ll*ans*C(n - 1 - sick.back() + pre, pre)%MOD; return ans; } };