A
容易发现每一个数分一段是最优的,因为两个数或起来可能变小而绝不可能变大。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int n;
int main()
{
scanf("%d",&n);
long long ans=0;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
ans+=x;
}
printf("%lld",ans);
}
B
我的做法:
容易发现删除的时候肯定从小往大删,设删除的最后一个数为
i
i
i,那么前
i
−
1
i-1
i−1 个数最多保留
m
m
m 个,所以答案为:
∑
i
=
1
n
∑
j
=
1
min
(
i
−
1
,
m
)
C
i
−
1
j
\sum_{i=1}^n \sum_{j=1}^{\min(i-1,m)}C_{i-1}^j
i=1∑nj=1∑min(i−1,m)Ci−1j
拆一下就是个卷积的形式了:
∑
i
=
1
n
(
i
−
1
)
!
∑
j
=
1
min
(
i
−
1
,
m
)
1
j
!
1
(
i
−
1
−
j
)
!
\sum_{i=1}^n (i-1)! \sum_{j=1}^{\min(i-1,m)} \frac 1 {j!} \frac 1 {(i-1-j)!}
i=1∑n(i−1)!j=1∑min(i−1,m)j!1(i−1−j)!1
当时没多想,直接拷一手 ntt \text{ntt} ntt 板子就过了。
代码如下:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 3000010
#define mod 998244353
#define bin(x) (1<<(x))
int n,m;
int add(int x){return x>=mod?x-mod:x;}
int dec(int x){return x<0?x+mod:x;}
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int inv[maxn];
struct NTT{
int w[maxn];void prep(int lg){int N=bin(lg);
inv[1]=1;for(int i=2;i<=N;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1,wn;i<N;i<<=1){
w[i]=1;wn=ksm(3,(mod-1)/(i<<1));
for(int j=1;j<i;j++)w[i+j]=1ll*w[i+j-1]*wn%mod;
}
}
int limit,r[maxn];
void work(int lg){for(int i=1;i<bin(lg);i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lg-1));}
void dft(int *f,int lg,int type=0){
limit=bin(lg);if(type)reverse(f+1,f+limit);
for(int i=1;i<limit;i++)if(i<r[i])swap(f[i],f[r[i]]);
for(int mid=1,t;mid<limit;mid<<=1)for(int j=0;j<limit;j+=(mid<<1))for(int i=0;i<mid;i++)
{t=1ll*f[j+i+mid]*w[i+mid]%mod;f[j+i+mid]=dec(f[j+i]-t);f[j+i]=add(f[j+i]+t);}
}
}ntt;
int A[maxn],B[maxn];
struct POLY{
vector<int> a;int len;void rs(int ln){a.resize(len=ln);}POLY(){rs(0);}
int &operator [](int x){return a[x];}
void dft(int *A_,int lg,int ln){for(int i=0;i<bin(lg);i++)A_[i]=i<min(len,ln)?a[i]:0;ntt.dft(A_,lg);}
void idft(int *A_,int lg,int ln){ntt.dft(A_,lg,1);rs(ln);for(int i=0;i<ln;i++)a[i]=1ll*A_[i]*inv[bin(lg)]%mod;}
POLY Mul(POLY b,int ln){
int lg=ceil(log2(ln*2-1));ntt.work(lg);dft(A,lg,ln);b.dft(B,lg,ln);
for(int i=0;i<bin(lg);i++)B[i]=1ll*A[i]*B[i]%mod;b.idft(B,lg,ln);return b;
}
}F,G;
int fac[maxn],inv_fac[maxn];
void work(){
fac[0]=inv_fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=1ll*fac[i-1]*i%mod;
inv_fac[i]=1ll*inv_fac[i-1]*inv[i]%mod;
}
}
int main()
{
scanf("%d %d",&n,&m);
ntt.prep(ceil(log2(n<<2)));work();
F.rs(m+1);for(int i=0;i<=m;i++)F[i]=inv_fac[i];
G.rs(n+1);for(int i=1;i<=n;i++)G[i]=inv_fac[i-1];
F=F.Mul(G,n+1);
int ans=1;
for(int i=1;i<=n;i++)add(ans,1ll*F[i]*fac[i-1]%mod);
printf("%d",ans);
}
官方题解做法:
考虑最后剩下的元素数量,假设为 k k k,那么删掉的 n − k n-k n−k 个有 min ( m + 1 , k + 1 ) \min(m+1,k+1) min(m+1,k+1) 个位置可以插,也是用组合数求一下就可以了。
C
我的做法:
当时大力猜结论,假设 a = b a=b a=b 最优。
然后打了个表,发现 a a a 的选取是有单调性的,于是直接三分出最优解,然后居然就过了,证明并不是很会……
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long
int n,a[maxn],b[maxn];
ll ans=0;
ll check(int x){
for(int i=1;i<=n;i++)b[i]=abs(a[i]-x);
sort(b+1,b+n+1);
ll re=0;
for(int i=1;i<=n;i++)re+=2ll*i*b[i]-b[i];
return re;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],a[i]*=2;
int l=0,r=1e9;
while(r-l>2){
int mid1=l+(r-l+1)/3,mid2=mid1+(r-l+1)/3;
if(check(mid1)>check(mid2))l=mid1+1;
else r=mid2-1;
}
int ans=l;
if(check(ans)>check(l+1))ans=l+1;
if(check(ans)>check(r))ans=r;
printf("%lld",check(ans)%(int)(1e9+7));
}
官方题解做法:
题目相当于要求找到一个点 ( a , b ) (a,b) (a,b) 使其到所有其他点 ( A i , A j ) (A_i,A_j) (Ai,Aj) 的切比雪夫距离之和最小,而两个点 ( x , y ) , ( a , b ) (x,y),(a,b) (x,y),(a,b) 的切比雪夫距离等于 ( x + y , x − y ) , ( a + b , a − b ) (x+y,x-y),(a+b,a-b) (x+y,x−y),(a+b,a−b) 两点的曼哈顿距离除以 2 2 2,这个转化很秀,手玩一下就能证明出来。
令所有点转化为 ( A i + A j , A i − A j ) (A_i+A_j,A_i-A_j) (Ai+Aj,Ai−Aj),然后就是要找一个点 a , b a,b a,b 使其到所有其他点的曼哈顿距离之和最小,显然两维坐标可以分开考虑,以 x x x 坐标为例, a a a 一定是所有 A i + A j A_i+A_j Ai+Aj 的中位数,用二分 a a a 的大小用尺取法判断是否合法即可。
D
对于一个位置
d
d
d,考虑构造他的
E
G
F
EGF
EGF,设
x
i
x^i
xi 的系数为
k
k
k 次操作中
d
d
d 被操作了
i
i
i 次,最后
d
d
d 位上是
0
0
0 的方案数,注意到只有最后一次操作决定这一位是什么,即前
i
−
1
i-1
i−1 次操作可以随意操作,令
v
1
v1
v1 表示有
v
1
v1
v1 个操作使
d
d
d 变
0
0
0,有
v
2
v2
v2 个操作与
d
d
d 相关,那么
E
G
F
EGF
EGF 为:
v
1
∑
i
=
1
∞
v
2
i
−
1
x
i
i
!
=
v
1
e
v
2
x
−
1
v
2
v1\sum_{i=1}^{\infty}\frac {v2^{i-1}x^i} {i!}=v1\frac {e^{v2x}-1} {v2}
v1i=1∑∞i!v2i−1xi=v1v2ev2x−1
如果 A d = 0 A_d=0 Ad=0,那么还要 + 1 +1 +1,即加上 x 0 x_0 x0 表示可以不操作。
然后考虑分治 n t t ntt ntt 将这些 E G F EGF EGF 乘起来,将多项式维护成 ∑ i a i e i x \sum_i a_ie^{ix} ∑iaieix 的形式,发现计算方式其实和普通多项式相同。
最后的答案就是 x k x^k xk 的系数,对于第 i i i 项 a i e i x a_ie^{ix} aieix,他对 x k x^k xk 的贡献为 a i i k x k k ! × k ! = a i i k a_i\dfrac {i^kx^k} {k!}\times k!=a_ii^k aik!ikxk×k!=aiik,于是求和一下就做完了。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300010
#define mod 998244353
#define bin(x) (1<<(x))
int add(int x){return x>=mod?x-mod:x;}
int dec(int x){return x<0?x+mod:x;}
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int inv[maxn];
struct NTT{
int w[maxn];void prep(int lg){int N=bin(lg);
inv[1]=1;for(int i=2;i<=N;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1,wn;i<N;i<<=1){
w[i]=1;wn=ksm(3,(mod-1)/(i<<1));
for(int j=1;j<i;j++)w[i+j]=1ll*w[i+j-1]*wn%mod;
}
}
int limit,r[maxn];
void work(int lg){for(int i=1;i<bin(lg);i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lg-1));}
void dft(int *f,int lg,int type=0){
limit=bin(lg);if(type)reverse(f+1,f+limit);
for(int i=1;i<limit;i++)if(i<r[i])swap(f[i],f[r[i]]);
for(int mid=1,t;mid<limit;mid<<=1)for(int j=0;j<limit;j+=(mid<<1))for(int i=0;i<mid;i++)
{t=1ll*f[j+i+mid]*w[i+mid]%mod;f[j+i+mid]=dec(f[j+i]-t);f[j+i]=add(f[j+i]+t);}
}
}ntt;
int A[maxn],B[maxn];
struct POLY{
vector<int> a;int len;void rs(int ln){a.resize(len=ln);}POLY(){rs(0);}
int &operator [](int x){return a[x];}
void dft(int *A_,int lg,int ln){for(int i=0;i<bin(lg);i++)A_[i]=i<min(len,ln)?a[i]:0;ntt.dft(A_,lg);}
void idft(int *A_,int lg,int ln){ntt.dft(A_,lg,1);rs(ln);for(int i=0;i<ln;i++)a[i]=1ll*A_[i]*inv[bin(lg)]%mod;}
POLY Mul(POLY b,int ln){
int lg=ceil(log2(ln*2-1));ntt.work(lg);dft(A,lg,ln);b.dft(B,lg,ln);
for(int i=0;i<bin(lg);i++)B[i]=1ll*A[i]*B[i]%mod;b.idft(B,lg,ln);return b;
}
}F;
int n,m,k,a[maxn];
int v1[maxn],v2[maxn];
POLY solve(int l,int r){
if(l==r){
POLY re;re.rs(v2[l]+1);
if(!v2[l])return re[0]=1,re;
re[0]=1ll*((a[l]?mod:v2[l])-v1[l])*inv[v2[l]]%mod;
re[v2[l]]=1ll*v1[l]*inv[v2[l]]%mod;
return re;
}
int mid=l+r>>1;
POLY p1=solve(l,mid),p2=solve(mid+1,r);
return p1.Mul(p2,p1.len+p2.len-1);
}
int main()
{
scanf("%d %d %d",&n,&m,&k);ntt.prep(ceil(log2((m+1)<<1)));
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1,x,y;i<=m;i++)scanf("%d %d",&x,&y),v1[x]+=(y==0),v2[x]++;
for(int i=1;i<=n;i++)if(a[i]&&!v1[i])return printf("0"),0;
F=solve(1,n);int ans=0;
for(int i=1;i<=m;i++)ans=(ans+1ll*F[i]*ksm(i,k)%mod)%mod;
printf("%d",ans);
}