牛客挑战赛43 部分题解

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 i1 个数最多保留 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=1nj=1min(i1,m)Ci1j

拆一下就是个卷积的形式了:
∑ 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=1n(i1)!j=1min(i1,m)j!1(i1j)!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 nk 个有 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,xy),(a+b,ab) 两点的曼哈顿距离除以 2 2 2,这个转化很秀,手玩一下就能证明出来。

令所有点转化为 ( A i + A j , A i − A j ) (A_i+A_j,A_i-A_j) (Ai+Aj,AiAj),然后就是要找一个点 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 i1 次操作可以随意操作,令 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=1i!v2i1xi=v1v2ev2x1

如果 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);
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/108703164
今日推荐