暑假集训CSP提高模拟 ∫[0,6] (x^2)/6 dx

\[\text{暑假集训CSP提高模拟}\int^{6}_{0}\frac{x^{2}}{6}dx \]

关于这个东西怎么求的良心教程

含义:求出 \(f(x)=\frac{x^{2}}{6}\) 在区间 \([0,6]\) 内在 \(x\) 轴上方,\(f(x)\) 下方的图形面积.

解法:考虑到 “加速度变化面积等于速度变化量” 类似的思想,我们尝试构造一个 \(g(x)\) 使得 \(g'(x)=f(x)\),不难发现存在一个 \(g(x)=\frac{x^{3}}{18}\),这样我们就可以把求面积问题转化成求变化量问题,因此直接 \(g(6)-g(0)=12\)

非常好,你已经会积分了,现在来试试这个吧:

\[\int^{b}_{a}\frac{lnx\sin x}{x^3}dx \]

A.黑客

显然这个题里只有 \(999\) 放在复杂度里是有可能对的,要么是 \(999^{2}\) 要么是 \(999^{2}\log 999\),显然应该是前者.

考虑枚举全部的最简分数,然后乘上去,算的时候直接算当前分母/分子是最简分式的几倍(注意上下取整的选择),然后在分子分母的可取值区间取一个交集即可.

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=1e9+7;
signed main(){
//	freopen("T1.in","r",stdin);
//	freopen("test.out","w",stdout);
	int a,b,c,d,ans=0;
	scanf("%lld %lld %lld %lld",&a,&b,&c,&d);
	for(int i=1;i<=999;++i){
		for(int j=1;j<=999-i;++j){
			if(__gcd(i,j)==1){
				int l1=ceil(a*1.0/i),l2=ceil(c*1.0/j);
				int r1=floor(b*1.0/i),r2=floor(d*1.0/j);
				ans=(((i+j)*max(0ll,(min(r1,r2)-max(l1,l2)+1))%p)+ans)%p;
//				if(min(r1,r2)-max(l1,l2)>0)
//				cout<<i<<" "<<j<<" "<<max(l1,l2)<<" "<<min(r1,r2)<<" "<<endl;
			}
		}
	}
	printf("%lld\n",ans);
}

B.密码技术

你说得对但是这题我做过😢

注意到交换具有传递性,对 \((1,2),(2,3)\) 可交换的情况,可以考虑如下交换方式得到 \((1,3)\) :

1    2    3    3
2 -> 1 -> 1 -> 2
3    3    2    1

多摸几组可以得到规律:这样的交换是有传递性的,每当我们想交换两个不能直接交换的元素,仅仅需要以中间元素作为中介分别交换即可.

因为这个题元素不重复(话说当时发现元素重复的这个题在同阶范围下是不可做的)因此行和列交换互不影响,直接乘法原理乘一下就行了. 对于每一个能互相交换的连通块,不妨用并查集维护大小 \(s\),对答案的贡献即为 \(A^{s}_{s}=s!\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,K;
int a[51][51];
const int p=998244353;
class dsu{
	private:
		int fa[51],size[51];
	public:
		void clear(){
			for(int i=1;i<=n;++i){
				fa[i]=i;
				size[i]=1;
			}
		}
		int find(int id){
			if(id==fa[id]) return id;
			fa[id]=find(fa[id]);
			return fa[id];
		}
		void join(int x,int y){
			int a=find(x),b=find(y);
			if(a!=b){
				fa[a]=b;
				size[b]+=size[a];
				size[a]=0;
			}
		}
		bool isfa(int id){
			return id==fa[id];
		}
		int get_size(int id){
			return size[id];
		}
};
dsu h,l;
int frac[51];
signed main(){
//	freopen("T2.in","r",stdin);
	frac[0]=1;
	for(int i=1;i<=50;++i){
		frac[i]=frac[i-1]*i%p;
	}
	scanf("%lld %lld",&n,&K);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			scanf("%lld",&a[i][j]);
		}
	}
	h.clear();l.clear();
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			bool flag=true;
			for(int k=1;k<=n;++k){
				if(a[i][k]+a[j][k]>K){
					flag=false;break;
				}
			}
			if(flag) h.join(i,j);
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			bool flag=true;
			for(int k=1;k<=n;++k){
				if(a[k][i]+a[k][j]>K){
					flag=false;break;
				}
			}
			if(flag) l.join(i,j);
		}
	}
	int ans=1;
	for(int i=1;i<=n;++i){
		if(h.isfa(i)){
			ans=ans*frac[h.get_size(i)]%p;
		}
		if(l.isfa(i)){
			ans=ans*frac[l.get_size(i)]%p;
		}
	}
	printf("%lld\n",ans);
}

C.修水管

暴力做法是显然的,写一个深搜暴力转移即可(其实能写状压的,我怎么老是忘了能状压)

#include<bits/stdc++.h>
using namespace std;
#define double long double
int n,r;
int w[251];
double p[251];
double dfs(int now,int brok,double nowp){
	double res=0;
	if(now>r){ return 0;}
	if(nowp==0){ return 0;}
	double zc=0;if(brok){ res+=w[brok];zc=p[brok];p[brok]=0;}
	double nwp=1;
	for(int i=1;i<=n;++i){
		res+=dfs(now+1,i,nwp*p[i]);
		nwp*=(1-p[i]);
	}
	res+=dfs(now+1,0,nwp);
	if(brok){ p[brok]=zc;}
	return res*nowp;
}
int main(){
	int cases;cin>>cases;while(cases--){
		cin>>n>>r;
		for(int i=1;i<=n;++i){
			cin>>p[i]>>w[i];
		}
		printf("%.10Lf\n",dfs(0,0,1));
	}
}

这道题的正解是 DP,设计 \(f_{i,j}\) 表示前 \(i\) 个中选 \(j\) 个的概率,则最终的 \(h_i\) 可以通过枚举转移得出

要得出转移,必须要知道第 \(i\) 个数被选的概率 而这个东西等于 \(1\) 减去它不被选的概率,而不被选的概率是 \((1-p_i)^{r-j}\),表示可能选到它的 \(r-j\) 次都没选到(根据定义,有 \(j\) 次选的前面的数,因此不算在里面)

所以有:

\[f_{i,j}=(1−(1−p_{i})^{r−j+1})f_{i−1,j−1}+(1−p_{i})^{r−j}f_{i−1,j} \]

最后概率乘权值即可.

#include<bits/stdc++.h>
using namespace std;
int n,r;
double ans,p[251],w[251],f[251][251];
int main(){
	int cases;scanf("%d",&cases);while(cases--){
		scanf("%d %d",&n,&r);
		for(int i=1;i<=n;++i){
			scanf("%lf %lf",&p[i],&w[i]);
		}
		memset(f,0,sizeof f);
		ans=0;
		f[0][0]=1;
		double res=0;
		for(int i=0;i<=n-1;++i){
			for(int j=0;j<=(i<r?i:r);++j){
				res=pow(1-p[i+1],r-j);
				f[i+1][j]+=f[i][j]*res;
				if(j<r){
					f[i+1][j+1]+=f[i][j]*(1-res);
					ans+=f[i][j]*(1-res)*w[i+1];
				}
			}
		}
		printf("%.10lf\n",ans);
	}
}

D.货物搬运

posted @ 2024-07-31 17:03  HaneDaniko  阅读(22)  评论(1编辑  收藏  举报