JYUACM训练周赛6-题解
A题 B4261 [GESP202503 三级] 2025
这题有两种做法:
1.注意到x的范围很小,我们可以直接暴力枚举0-2025,看看是否成立就行了;
2.我们可以把 x 和 y 拆分成二进制数,并讨论他们其中一位的情况(00,01,10,11),我们可以发现在 & 和 | 运算后和运算前的答案是一样的,所以 y= 2025 - x
代码如下:
点击查看代码
#py代码
x=int(input())
print(2025-x)
B题 P1105 平台
1.这题是个模拟题,可以不需要排序,暴力枚举O(\(N^2\))就可以过
2.我们只需要一个结构体或者三个数组记录一下每个平台的高度和左右端点,对于每一个平台,我们遍历所有平台,看它高度低于当前高度,并且是否全部包含左端点(右端点),是的话记录它的编号。
3.这里要注意的是编号小的平台不一定被最先跳到,所以我们还要比较之后的符合情况平台是不是比我们的答案高,如果是就记录起来。
代码如下:
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10,M=1010;
struct nod{
    int h,l,r;
}a[M];
void solve(){
	int n,h,l,r;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>h>>l>>r;
        a[i]={h,l,r};
    }
    for(int i=1;i<=n;i++){
        h=a[i].h,l=a[i].l,r=a[i].r;
        int flag1=0;
        for(int j=1;j<=n;j++){
            if(i==j||h<=a[j].h)continue;
            if(a[j].l<l&&a[j].r>l){
                if(flag1==0)flag1=j;
                else if(a[j].h>a[flag1].h)flag1=j;
            }
        }
        cout<<flag1<<" ";
        int flag2=0;
        for(int j=1;j<=n;j++){
            if(i==j||h<=a[j].h)continue;
            if(a[j].l<r&&a[j].r>r){
                if(flag2==0)flag2=j;
                else if(a[j].h>a[flag2].h)flag2=j;
            }
        }
        cout<<flag2<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int _=1;
    //cin>>_;
    while(_--) solve();
    return 0;
}
C题 P3743 小鸟的设备
这题要求最大使用时间,这种求最值的题目,大多数情况都可以用到二分,我们考虑二分答案。我们设可使用 x 的时间,那么充电宝可以提供的能量就是res =x * p,对于第i个设备,它消耗的能量是a[i] * x,如果消耗的电量大于存储的能量b[i],那么我们将缺少的这部分能量从res里扣去,如果最后res不足,那么这个时间 x 过长。注意:如果消耗的总时间 \(\sum_{i=0}^{n} a[i]\) <= p ,那么设备就可以一直运转下去,否则一定会停止(因为随着时间增加,总能量始终是在减少的)
点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7,INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
const double eps=1e-6;
int n,m,k,kk,u,v,p;
int a[N],b[N];
bool check(double x){
	double res=x*p;
	for(int i=1;i<=n;i++){
		double tmp=a[i]*x-b[i];
		if(tmp<=0)continue;
		res-=tmp;
		if(res<0)return 0;
	}
	return 1;
}
void solve(){
    cin>>n>>p;
    int sum=0;
    for(int i=1;i<=n;i++)cin>>a[i]>>b[i],sum+=a[i];
    if(p>=sum){
    	cout<<-1<<endl;
    	return;
    }
    double l=0,r=1e18;
    while(fabs(l-r)>eps){
    	double mid=(l+r)/2;
    	if(check(mid))l=mid;
    	else r=mid;
    }
    printf("%.10f\n",l);
}
signed main(){
    //ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int _=1;
    // cin>>_;
    while(_--) solve();
    return 0;
}
D题 P1122 最大子树和
这题是一道经典的树形dp题,题目虽然没有规定根节点,但是因为这是一颗树,我们随便指定一个点为根节点就行,但选根节点不一定最优,最优情况可能不包含我们指定的那个根节点,所以我们要用dp进行记忆化搜索,并在最后遍历一边所有节点和它相应的子树的最大值。//这里还要注意的是,我们至少要选择一个节点,因为题目要求子树不为空
我们设dp[i]表示选择以i为根节点及其子树的最大收益,那么对于节点i的子树j,我们可以选择j,那么它对i的贡献就是dp[i];如果不选j,那么它对i的贡献就是0,所以我们得到了转移方程:dp[i] = max(dp[i], dp[i]+dp[j]);
代码如下:
点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7,INF=0x3f3f3f3f;
int n,m,k,kk,s;
int h[N],ne[N*2],e[N*2],w[N*2],idx;
int dp[N];//dp[i]表示选择以i为根的子树最大的收益
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int p,int from){
	dp[p]=w[p];//注意:子树不能为空
	for(int i=h[p];~i;i=ne[i]){
		int j=e[i];
		if(j==from)continue;
		dfs(j,p);
		dp[p]+=max(0ll,dp[j]);
	}
}
void solve(){
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)cin>>w[i];
    int a,b;
    for(int i=1;i<n;i++){
    	cin>>a>>b;
    	add(a,b),add(b,a);
    }
    dfs(1,0);
    int res=-inf;
    for(int i=1;i<=n;i++)res=max(res,dp[i]);
    cout<<res<<endl;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int _=1;
    // cin>>_;
    while(_--) solve();
    return 0;
}
E题 P2071 座位安排
这题要用到匈牙利算法,没有学过的同学要去学一下了:ACWing-算法基础课-AcWing 861. 二分图的最大匹配 
这道题唯一和模板不一样的地方在于每排可以坐两个人,而模板的话只能一对一进行匹配,既然这样,我们多开一维来存每排座位的具体位置就可以了。
点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10,M=2010,mod=1e9+7,INF=0x3f3f3f3f;
int n,m,k,kk,s;
int h[M*2],ne[M*4],e[M*4],idx;
int st[2*M],match[M*2][2];
void add(int a,int b){
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool find(int x){// 匈牙利算法匹配二分图
	for(int i=h[x];~i;i=ne[i]){
		int j=e[i];
		if(st[j])continue; //第j排座位没有被考虑才进行下一步
		st[j]=1;
		//尝试匹配第一个位置,如果这个位置没有被坐过或者该位置坐着的人可以改坐其他位置
		if(match[j][0]==0||find(match[j][0])){//尝试匹配第一个位置
			match[j][0]=x;
			return 1;
		}
		//第二个位置同理
		if(match[j][1]==0||find(match[j][1])){
			match[j][1]=x;
			return 1;
		}
	}
	return 0;
}
void solve(){
    cin>>n;
    memset(h,-1,sizeof h);
    int a,b;
    for(int i=1;i<=n*2;i++){
    	cin>>a>>b;
    	add(i,a),add(i,b);
    }
    int res=0;
    for(int i=1;i<=n*2;i++){
    	memset(st,0,sizeof st);// 每个人只能考虑各位置一次:多看一眼看一眼就会爆炸。
    	if(find(i))res++;//看看第i个人可不可以找到想坐的位置
    }
    cout<<res<<endl;
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int _=1;
    // cin>>_;
    while(_--) solve();
    return 0;
}
F题 P1414 又是毕业季II
时间过的好快呀,马上迎新就变成了我们,会遇到实力强劲的师弟师妹们吗?
1.这题是个数学题,我们怎么知道n个数中哪k个数构成的gcd最大呢?
2.其实我们不需要知道是哪k个数,对于一个gcd,构成它的k个数肯定都包含这个因数gcd,所以我们只需要对每个数分解出它们的因数,最后我们从最大值往下找第一个符合的位置即可
3.需要注意的是,随着k的增大,可以肯定的是gcd不可能会增大,如果每次都从最大值开始找,会导致超时,我们只要接着上一次的gcd值继续往下找就可以了。
点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+10,M=2010,mod=1e9+7,INF=0x3f3f3f3f;
int n,m,k,kk,s;
int cnt[N];
void divide(int x){
	for(int i=1;i<=x/i;i++){
		if(x%i==0){
			cnt[i]++;
			//如果x不是i的平方,x/i也是x的因子
			//如果去掉的话sqrt(x)以上的x的因数会被漏掉
            //如果x是i的平方只记录i作为一次因子
			if(x!=i*i) cnt[x/i]++;
		}
	}
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++){
    	cin>>k;
    	divide(k);
    }
    int maxv=1e6;
    for(int i=1;i<=n;i++){
    	while(cnt[maxv]<i)maxv--;
    	cout<<maxv<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int _=1;
    // cin>>_;
    while(_--) solve();
    return 0;
}
 
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号