牛客练习赛41 题解

BG


最近好水啊感觉浑身无力仿佛已经退役。。对于自己暴力选手的本质已经有个清晰的认知了

A - 翻硬币问题


敢写就能A系列
容易发现结果只和奇偶性有关。如果n和m奇偶性不同肯定不能赢,如果相同肯定改成不同。
那么就是如果能一发赢掉就Awin,不然就输lor

Code


#include 
#include 
#include 

int main(void) {
	int T; scanf("%d",&T);
	for (;T--;) {
		int n,m; scanf("%d%d",&n,&m);
		if (n==m) puts("Yes");
		else puts("No");
	}
	return 0;
}

B - 666RPG


水题,我们发现所有数字的和值域是[-666300,666300]的,于是暴力dp计数就可以了

Code


#include 
#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))

const int MOD=100000007;
const int N=670*300;

int f[2][N*2+505],a[305];

void upd(int &x,int y) {
	x=(x+y)%MOD;
}

int main(void) {
	int n; scanf("%d",&n);
	rep(i,1,n) scanf("%d",&a[i]);
	int now=0;
	f[0][0+N]=1;
	rep(i,1,n) {
		now^=1;
		fill(f[now],0);
		rep(j,-199800,199800) if (j!=666) {
			if (-j!=666) upd(f[now][j+N],f[!now][-j+N]);
			if (j-a[i]>=-199800&&j-a[i]<=199800&&j-a[i]!=666) upd(f[now][j+N],f[!now][j-a[i]+N]);
		}
		int ggg=0;
	}
	printf("%d\n", f[now][-666+N]);
	return 0;
}

C - 抓捕盗窃犯


无聊题,根据攻♂受关系(雾,并查集维护最受的那个点就可以了。最优的m个就排序嘛

Code


#include 
#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int N=200005;

int fa[N],c[N];

LL size[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int find(int x) {
	return (!fa[x])?x:(fa[x]=find(fa[x]));
}

bool cmp(int x,int y) {
	return size[x]>size[y];
}

int main(void) {
	int n=read(),m=read();
	rep(i,1,n) size[i]=read();
	rep(i,1,n) {
		int x=read();
		int fi=find(i);
		int fx=find(x);
		if (fi!=fx) {
			fa[fi]=fx;
			size[fx]+=size[fi];
		}
	}
	rep(i,1,n) if (find(i)==i) {
		c[++c[0]]=i;
	}
	std:: sort(c+1,c+c[0]+1,cmp);
	LL ans=0;
	rep(i,1,c[0]) {
		if (m) {
			ans+=size[c[i]];
			m--;
		}
	}
	printf("%lld\n", ans);
	return 0;
}

D - 最小相似度


非常巧妙的思路

我们把给定序列的桶记作A[]
注意到异或满足 A ⊕ B = C ↔ A ⊕ C = B A\oplus B=C\leftrightarrow A\oplus C=B AB=CAC=B,我们二分一个答案然后把所有可能的异或结果找出来扔桶里,记作B[]
对A[]和B[]做异或卷积,记得到C[],那么若C中存在方案数恰好为n的位置说明这里是一个可行解
于是这样做是 O ( n log ⁡ ( n ) log ⁡ ( log ⁡ ( n ) ) ) O(n\log(n)\log(\log(n))) O(nlog(n)log(log(n)))的。

Code


#include 
#include 
#include 
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

const int MOD=998244353;
const int ny2=499122177;
const int N=2000005;

int a[N],b[N],c[N],lim;

char s[N];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void FWT(int *a,int n,int f) {
	for (int i=1;i<n;i<<=1) {
		for (int j=0;j<n;j+=(i<<1)) {
			for (int k=0;k<i;++k) {
				int u=a[j+k],v=a[j+k+i];
				a[j+k]=(u+v>=MOD)?(u+v-MOD):(u+v);
				a[j+k+i]=(u-v+MOD>=MOD)?(u-v):(u-v+MOD);
				if (f==-1) {
					a[j+k]=1LL*a[j+k]*ny2%MOD;
					a[j+k+i]=1LL*a[j+k+i]*ny2%MOD;
				}
			}
		}
	}
}

bool check(int n,int mid) {
	for (register int i=0;i<lim;++i) b[i]=(c[i]<=mid);
	FWT(b,lim,1);
	for (register int i=0;i<lim;++i) b[i]=1LL*b[i]*a[i]%MOD;
	FWT(b,lim,-1);
	for (register int i=0;i<lim;++i) if (b[i]==n) {
		return true;
	}
	return false;
}

int main(void) {
	freopen("data.in","r",stdin);
	int n=read(),m=read(); lim=(1<<m);
	for (int i=0;i<lim;++i) c[i]=c[i>>1]+(i&1);
	rep(i,1,n) {
		scanf("%s",s); int x=0;
		rep(j,0,m-1) x+=((s[j]=='1')<<j);
		a[x]++;
	}
	FWT(a,lim,1);
	int l,r,ans=m;
	for (l=0,r=m;l<=r;) {
		int mid=(l+r)>>1;
		if (check(n,mid)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%d\n", ans);
	return 0;
}

E - 球的体积并


高中知识系列

首先判掉相离的情况,显然就是0
然后判掉包含的情况,显然就是小球体积
画图观察我们要求的到底是什么,可以发现是两个共底面的球缺,直观地讲就是一刀切苹果切下来的部分(当然你说留下的部分好像也没毛病

图片来源见水印,球缺指的就是缺掉的那一部分

考虑怎么求这个体积。我们以球心为原点,平行于球缺底面任意一方向为x轴,垂直于球缺底面方向为y轴
记R为球的半径,h为球缺高,那么球缺的体积就是 ∫ R − h R ( π ⋅ x 2 ) d y \int_{R-h}^{R} \left(\pi\cdot x^2\right)dy RhR(πx2)dy
我们的坐标实际上是取在球的某一个剖面的边界上
注意到 x 2 + y 2 = R 2 x^2+y^2=R^2 x2+y2=R2,因此 x 2 = R 2 − y 2 x^2=R^2-y^2 x2=R2y2
原柿等价于 ∫ R − h R [ π ⋅ ( R 2 − y 2 ) ] d y \int_{R-h}^{R} \left[\pi\cdot\left(R^2-y^2\right)\right]dy RhR[π(R2y2)]dy
于是用我们的高中知识即可知道 V = π R h 2 − 1 3 π h 3 V=\pi Rh^2-\frac{1}{3}\pi h^3 V=πRh231πh3
然后它们的交就是两个球缺的体积了,至于h同样可以用余弦定理这个高中知识解决,这里就不细说了

当然还有别的做法,比如先算球冠面积然后比出(球缺+圆锥)的体积,减一减就可以了吧

Code


#include 
#include 
#include 
#include 

const double pi=acos(-1);
 
const int MAX=100+10;
const int inf=1e9+7;
 
struct Point {
	double x,y,z,r;
} w,s;
 
int n;
 
double dis(Point p,Point q) {
	return sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)+(p.z-q.z)*(p.z-q.z));
}
 
int main(void) {
	scanf("%lf%lf%lf%lf",&w.x,&w.y,&w.z,&w.r);
	scanf("%lf%lf%lf%lf",&s.x,&s.y,&s.z,&s.r);
	double ans=(4.0/3)*pi*(w.r*w.r*w.r+s.r*s.r*s.r);
	double d=dis(s,w);
	if (d>=s.r+w.r) ans=ans;
	else if (d+w.r<=s.r) ans-=4.0/3*pi*w.r*w.r*w.r;
	else if (d+s.r<=w.r) ans-=4.0/3*pi*s.r*s.r*s.r;
	else {
		double co=(s.r*s.r+d*d-w.r*w.r)/(2.0*d*s.r);
		double h=s.r*(1-co);
		ans-=(1.0/3)*pi*(3.0*s.r-h)*h*h;
		co=(w.r*w.r+d*d-s.r*s.r)/(2.0*d*w.r);
		h=w.r*(1-co);
		ans-=(1.0/3)*pi*(3.0*w.r-h)*h*h;
	}
	printf("%.10lf\n",ans);
	return 0;
}

F - 简单数学题


留坑待更

你可能感兴趣的:(c++)