2023牛客OI赛前集训营-提高组(第二场)

2023牛客OI赛前集训营-提高组(第二场)

T1

题意,给定正整数 n n n,计算 n n n个元素的集合 1 , 2 , ⋯   , n {1,2,\cdots,n} 1,2,,n,求所有非空子集和的乘积取模 998244353 998244353 998244353后的结果。

其中 1 ≤ n ≤ 200 1\le n \le200 1n200

首先,该集合的非空子集有 2 n − 1 2^n-1 2n1个,但子集和的最大值只有 n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n(n1)

,所以肯定有重复的子集和。我们设 f i , j f_{i,j} fi,j表示从前面 i i i个数中取到值为 j j j的方案数。显而易见有两种情况,选和不选,那么 f i , j = ( f i − 1 , j + ( j > = i ) ∗ f i − 1 , j − i ) % ( m o d − 1 ) f_{i,j}=(f_{i-1,j}+(j>=i)*f_{i-1,j-i})\%(mod-1) fi,j=(fi1,j+(j>=i)fi1,ji)%(mod1)

此处记得模 ( m o d − 1 ) (mod-1) (mod1)而不是 m o d mod mod,下面给出详细证明。

费马小定理: a p − 1 ≡ 1 ( m o d p ) {a^{p-1}\equiv 1(mod p)} ap11(modp)

利用同乘性可以推出 a n ∗ ( p − 1 ) ≡ 1 ( m o d p ) {a^{n*(p-1)}\equiv 1(mod p) } an(p1)1(modp)

所以在 m o d mod mod意义下,想使 x a ≡ x b ( m o d p ) {x^a\equiv x^b(mod p)} xaxb(modp),就得让 a ≡ b ( m o d ( p − 1 ) ) a\equiv b(mod (p-1)) ab(mod(p1)) ,至此证毕。

时间复杂度为 O ( n 3 ) \mathcal O(n^3) O(n3)

#include
using namespace std;
long long n,f[500+10][125250+10],maxn=0,ans=1;
const long long mod=998244353;
long long read()
{
    long long s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        s=s*10+(ch-'0'),ch=getchar();
    return s*w;
}
long long ksm(long long a,long long b)
{
    long long sum=1;
    while(b)
    {
        if(b&1)
            sum=sum*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return sum;
}
int main()
{
    n=read();
    f[0][0]=1;
    maxn=(n+1)*n/2;
    for(int i=1;i<=n;++i)
        for(int j=0;j<=maxn;++j)
        {
            f[i][j]=f[i-1][j];
            if(j>=i)
                f[i][j]=(f[i][j]+f[i-1][j-i])%(mod-1);
        }
    for(int i=1;i<=maxn;++i)
        ans=ans*ksm(i,f[n][i])%mod;
    printf("%lld",ans);
    return 0;
}

T2

手推了几组数据就会发现,存在一个区间 [ l , r ] [l,r] [l,r] ∑ i = l r v i > ( r − l + 1 + d ) ∗ k \sum\limits_{i =l}^{r} v_i>(r-l+1+d)*k i=lrvi>(rl+1+d)k,就输出 N O NO NO,将这个式子转换成

∑ i = l r ( v i − k ) > k ∗ d \sum\limits_{i=l}^{r}(v_i-k)>k*d i=lr(vik)>kd,用 w i w_i wi表示 v i − k v_i-k vik,也就是 ∑ i = l r w i > k ∗ d \sum\limits_{i=l}^{r}w_i>k*d i=lrwi>kd,前面就可以用线段树维护最大子段和。

时间复杂度为 O ( q log ⁡ n ) \mathcal O(q \log n) O(qlogn)

#include
using namespace std;
int n,q,k,d;
struct node
{
    int l,r;
    long long sum,maxl,maxr,maxn;
}t[5000000+10];
void build(int root,int l,int r)
{
    t[root].l=l;
    t[root].r=r;
    if(l==r)
    {
        t[root].sum=-k;
        return ;
    }
    int mid=(l+r)>>1;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    t[root].sum=t[root*2].sum+t[root*2+1].sum;
}
void add(int root,int l,int r,int pos,int val)
{
    if(l==r)
    {
        t[root].sum+=val;
        t[root].maxn=t[root].maxl=t[root].maxr=max(0ll,t[root].sum);
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
        add(root*2,l,mid,pos,val);
    else    
        add(root*2+1,mid+1,r,pos,val);
    t[root].maxl=max(t[root*2].maxl,t[root*2].sum+t[root*2+1].maxl);
    t[root].maxr=max(t[root*2+1].maxr,t[root*2+1].sum+t[root*2].maxr);
    t[root].maxn=max(t[root*2].maxr+t[root*2+1].maxl,max(t[root*2].maxn,t[root*2+1].maxn));
    t[root].sum=t[root*2].sum+t[root*2+1].sum;
}
int read()
{
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        s=s*10+(ch-'0'),ch=getchar();
    return s*w;
}
int main()
{   
    n=read(),q=read(),k=read(),d=read();
    build(1,1,n);
    while(q--)
    {
        int x=read(),y=read();  
        add(1,1,n,x,y);
        if(t[1].maxn<=1ll*k*d)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

T3、T4

未补。

你可能感兴趣的:(算法)