2019CSP-S模拟

T1

description

给定一个正整数 S,现在要求你选出若干个互不相同的正整数,使得它们的和不大于 S,而
且每个数的因数(不包括本身)之和最大。

input

输入一个数 S。

output

输出一个数 ans

样例

sample input 1
4
1.5 sample output 1
3
1.6 sample input 2
6
1.7 sample output 2
6

data range

对于 30% 的数据,S ≤ 10。
对于 100% 的数据,S ≤ 1000。

思路

典型背包问题

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int s;
int sum[1005];
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
signed main(){
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	s=read();
	for(int i=1;i<=s;i++){
		for(int j=1;j<=i/2;j++){//任何一个因数都不会超过i/2
			if((i%j)==0){
				sum[i]+=j;//预处理出因数和
			}
		}
	}
	for(int i=1;i<=s;++i){//枚举当前
		for(int j=1;j<=i/2;++j){
			sum[i]=max(sum[i],sum[j]+sum[i-j]);//sum类似于前缀和
		}
	}
	printf("%lld",sum[s]);
	return 0;
}

T2T2

思路:二分+单调队列优化

单调队列一般是具有单调性的队列,有单调递增和单调递减两种,一般来讲,队列的队首是整个队列的最大值或最小值
实现:若队列为空,将A[i]从队尾入队
若队列不为空,将比A[i]大的元素都从队尾弹出,然后把A[i]入队
若队列不为空且A[i]大于队尾,则直接从队尾把A[i]入队

代码

sum:减去平均值后的前缀和
check中枚举

  1. 注意i是枚举的右端点
  2. i − L i-L iL i − R i-R iR枚举左端点所在的区间
  3. q u e que que单增队列维护序列中最小的前缀和 ( s u m (sum (sum ) ) )
    注意 单调队列维护仅适用于只考虑最大最小值,弹出队列状态都对答案不产生任何影响时(决策单调性)
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+50;
int n,L,R,a[N];
double mid;
inline int read()
{
    char ch = getchar();int i = 0, f = 1;
    while (!isdigit(ch)){if (ch == '-')f = -1;ch = getchar();}
    while (isdigit(ch)){i = (i << 1) + (i << 3) + ch - '0';ch =getchar();}
    return i * f;
}
bool check(double v){
	static double sum[N],que[N];
	static int head,tail,pos[N];
	head=1;tail=0;
	for(int i=1;i<=n;i++){
		sum[i]=sum[i-1]+1.0*a[i]-v;
	}
	for(int i=L;i<=n;i++){
		while(head<=tail&&que[tail]>=sum[i-L]){
			tail--;//如果单调队列最末大于现在值,不断出队以维护单调性
		}
		while(head<=tail&&pos[head]+R<i){//pos[head]<i-R此时已经不可能作为左端点,出队
			head++;
		}
		que[++tail]=sum[i-L];
		pos[tail]=i-L;
		if(sum[i]-que[head]>=0){
			return true;
		}
	}
	return false;
}
int main(){
	n=read(),L=read(),R=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
	}
	double l=0,r=1e6;
	for(int i=1;i<=100;i++){
		mid=(l+r)/2;
		if(check(mid)){
			l=mid;
		}
		else	r=mid;
	}
	printf("%.4lf\n",mid);
	return 0;
}

T3

题目[华莱士]题目描述
思路

判断是否能加入一条边时,如果这条边的两端已经联通,我们需要知道这个联通块是否有环。如果不连通,那么可以加入这条边,而且通过两端的联通块是否有环可以得到新的联通块是否有环。
用并查集维护连通性,额外维护联通块里是否有环即可。

  1. 边的两端是同一棵树:判环;有环:不连;没环:连
  2. 边的两端是两棵树:判环;两个都有环:不连;其余:连
代码

book数组记录有没有环
fa维护并查集
tot连的边数 ( ( (树上还要带一条边,故不是 n − 1 ) n-1) n1)
并查集合并时合并环的信息

#include <bits/stdc++.h>
using namespace std;
int fa[500050],book[500050];

struct node{
	int x,y,w;
}a[500050];
bool cmp(const node &a,const node &b){
	return a.w<b.w;
}
inline char nc(){//超级快读
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
int getf(int x){
	return fa[x]==x?x:fa[x]=getf(fa[x]);
}
int main(){
	int n,m,i,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)  fa[i]=i;
	for(i=1;i<=m;++i)  scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
	sort(a+1,a+m+1,cmp);//按边权排序
	long long tot=0,ans=0;
	for(int i=1;i<=m;i++){
		x=getf(a[i].x),y=getf(a[i].y);
		if(x==y&&!book[x]){
			book[x]=1;tot++;ans+=a[i].w;
		}
		else{
			if(book[x]&&book[y])	continue;
			fa[x]=y;book[y]=book[x]|book[y];
			tot++;ans+=a[i].w;
		}
	}
	if(tot!=n){
		printf("No"); 
	}
	else{
		printf("%lld",ans);
	}
	return 0;
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值