题意,给定正整数 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 1≤n≤200
首先,该集合的非空子集有 2 n − 1 2^n-1 2n−1个,但子集和的最大值只有 n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n∗(n−1)
,所以肯定有重复的子集和。我们设 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=(fi−1,j+(j>=i)∗fi−1,j−i)%(mod−1)。
此处记得模 ( m o d − 1 ) (mod-1) (mod−1)而不是 m o d mod mod,下面给出详细证明。
费马小定理: a p − 1 ≡ 1 ( m o d p ) {a^{p-1}\equiv 1(mod p)} ap−1≡1(modp)
利用同乘性可以推出 a n ∗ ( p − 1 ) ≡ 1 ( m o d p ) {a^{n*(p-1)}\equiv 1(mod p) } an∗(p−1)≡1(modp)
所以在 m o d mod mod意义下,想使 x a ≡ x b ( m o d p ) {x^a\equiv x^b(mod p)} xa≡xb(modp),就得让 a ≡ b ( m o d ( p − 1 ) ) a\equiv b(mod (p-1)) a≡b(mod(p−1)) ,至此证毕。
时间复杂度为 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;
}
手推了几组数据就会发现,存在一个区间 [ 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=l∑rvi>(r−l+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=l∑r(vi−k)>k∗d,用 w i w_i wi表示 v i − k v_i-k vi−k,也就是 ∑ i = l r w i > k ∗ d \sum\limits_{i=l}^{r}w_i>k*d i=l∑rwi>k∗d,前面就可以用线段树维护最大子段和。
时间复杂度为 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;
}
未补。