noip 模拟 y

很考验观察力的一道题
首先发现,若每个人都给左边人小球,那每个人给的小球数就可以都减去一
形式化的,我们只需要考虑存在一个人给的小球数为零的情况
还是按套路来
可以把式子组合意义化(组合意义化后式子总会好推很多)
其实就是每个人从剩下的小球中选一个的总方案
此时选球的情况只有两种:

  • 从原来剩下的小球中选 (设为情况 0 )
  • 从右边的人给的球中选 (设为情况 1 )

那就可以给每个人定义两次分别表示上面的两种情况
又因为是环,转移结束的话我们是需要知道 1 号的选球情况的
那我们可以倾定 1 号的选球情况,最后从该情况索取我们要的答案就好了
那回到初始
我们考虑的仅是存在一个人给的小球数为零的情况
直接用所有情况减去全部给小球的情况就好了
最后考虑两人之间的转移

\(f_{i-1,1}->f_{i,0}\)
它的实际意义是第 \(i-1\) 个人已经选好了
第 \(i\) 个人准备从自己剩下的小球中选,是不考虑 \(i-1\) 给的小球的
但因为 \(i-1\) 给的小球会影响情况数,所以

\[f_{i,0}+=f_{i-1,1}(a_{i-1}-(lim)+1) \]

\(f_{i-1,0}->f_{i,0}\)
\(i-1\) 要从自己剩下的小球中选,\(i\) 也要从自己剩下的小球中选,但只能计算 \(i-1\) 的贡献

\[f_{i,0}+=f_{i-1,0}\sum_{k=0}^{a_{i-1}-(lim)}k \]

\(f_{i-1,1}->f_{i,1}\)
\(i-1\) 已经计算过贡献,\(i\) 要从 \(i-1\) 给的球中选

\[f_{i,1}+=f_{i-1,1}\sum_{k=(lim)}^{a_{i-1}}k \]

\(f_{i-1,0}->f_{i,1}\)
\(i-1\) 的贡献和 \(i\) 的贡献都在这次计算
则:

\[f_{i,1}+=f_{i-1,0}\sum_{k=(lim)}^{a_{i-1}}k(a_{i-1}-k) \]

对于转移方程中的 \((lim)\) 即为你倾定的总限,最后记得容斥

Code
#include <bits/stdc++.h>
#define re register
#define db double
#define int long long
// #define ll long long
#define fr first
#define sc second
#define pb push_back
#define fail { puts("No"); continue; }
#define pir make_pair
#define F(i,a,b) for(re int i=a;i<=b;++i)
#define D(i,a,b) for(re int i=a;i>=b;--i)
#define E(i,a) for(re int i=head[a];i;i=edge[i].nxt)
using namespace std;
const int maxn=3E7+10;
const int INF=1e9+10;
const int LS=1e5;
const int mol=1e9+7;
inline int qpow(int a,int b) { int ans=1; while(b) { if(b&1) (ans*=a)%=mol; (a*=a)%=mol; b>>=1; } return ans; }
inline int read(){
    int x=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9') w=(ch=='-')?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*w;
}

int n,a[1000010],f[2][1000010],inv2=qpow(2,mol-2),inv6=qpow(6,mol-2);
inline int sum(int n) { return ((n+1)%mol*n%mol*inv2%mol)%mol; }
inline int sm(int n) { return (n%mol*(n+1)%mol*(2*n%mol+1)%mol)*inv6%mol; }
inline int ls(int k1,int k2) {
    f[k1][1]=1; f[k1^1][1]=0;
    for(re int i=1,nt;i<=n;i++) {
        nt=(i==n)? 1:i+1;
        f[0][nt]=(f[1][i]*((a[i]-k2+1)%mol)%mol+f[0][i]*sum(a[i]-k2)%mol)%mol;
        f[1][nt]=(f[1][i]*sum(a[i])%mol+(f[0][i]*(a[i]*sum(a[i])%mol-sm(a[i]))%mol)%mol)%mol;
    }
    return f[k1][1];
}
signed main(void) {
    freopen("y.in","r",stdin); freopen("y.out","w",stdout);
    n=read(); for(re int i=1;i<=n;i++) a[i]=read();
    int ans=ls(1,0)+ls(0,0)-ls(1,1)-ls(0,1);
    printf("%lld\n",(ans%mol+mol)%mol);
}
上一篇:NOIP模拟78


下一篇:NOIP挑战赛-2 比赛记录