10.26离线赛题解

2 篇文章 0 订阅
1 篇文章 0 订阅

这次的离线赛不是内部的题,可以放出来了。
A题:
这题作为联赛第一题是偏难的,这题其实是有联赛第二题难度
反倒是B题水的一B。。
我们可以清晰的知道,
本题需要求解的是: Σsum[i]Σnum[i] 的最大值
由于数据范围的缘故,
直接暴力肯定不行(这不是废话)
我首先看到这题想到的是贪心
对于每行i都选取该值最大的
最后发现这是个错误的贪心
再仔细想想这题貌似不能贪心
于是想到了dp
然而这题dp是n^2*m
会炸(不过倒是有40分)
于是乎。。
我们知道其实这种题型其实解法无非两种
1.枚举分子(母),贪心分母(子)
2.就是这题的解法了
我们假设可以挖金矿时平均值可以达到k
于是可以得出:
Σsum[i]Σnum[i] ≥k
于是乎
Σsum[i]≥k*Σnum[i]
and then:
Σsum[i]-k*Σnum[i]≥0
end!!!!!!
Σ(sum[i]-k*num[i])≥0
于是在二分答案之后,只需在每行找出sum-k*num的最大值就行了
一波代码来袭:

#include<bits/stdc++.h>
int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    int a[n+5][m+5];double l=0,r=1e14;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    while(r-l>=0.00001){//“for(int i=0;i<100;i++){”其实也可以,但是太骚。。而且慢
        double mid=(l+r)/2,res=0;
        for(int i=1;i<=n;i++){
            double mx=0,tmp=0;
            for(int j=1;j<=m;j++){
                tmp+=a[i][j]-mid;
                if(tmp>mx||j==1)mx=tmp;
            }res+=mx;
        }if(res>=0)l=mid;
        else r=mid;
    }printf("%.4lf\n",l);
    return 0;
}

B题:道路规划
话说其实刚看到这题的时候
我是被它所谓的交叉给吓到了
但其实只要把南部城市当成val,
映射一下,LIS就能秒掉这题了
水题不解释。。。
代码:

#include<bits/stdc++.h>
using namespace std;
#define M 100005
int a[M],b[M],id[M],dp[M],val[M];
int n;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){scanf("%d",&b[i]);id[b[i]]=i;}
    val[0]=n+1;
    for(int i=1;i<=n;i++)val[n-i+1]=id[a[i]];
    int ans=0,len=0;dp[++len]=val[1];
    for(int i=2;i<=n;i++){//标准O(n*logn)LIS
        if(val[i]>dp[len])dp[++len]=val[i];
        else {
            int tmp=upper_bound(dp+1,dp+len+1,val[i])-dp;
            dp[tmp]=val[i]; 
        }
    }printf("%d\n",len);
    return 0;
}

C题:排队
话说这题其实也不是很难
考试的时候其实是想到倍增了的
但是因为 我是弱鸡的缘故 我逗比的认为O(n)的1号操作会超时,于是就没敲
其实这题的解法是很好想的
完全不需要线段树,数状数组什么的
其实只用后续遍历成一个数组以后
2操作倍增跳不解释
1操作其实根本不用管那么多,直接一个个塞就行了
(一次1操作最多可以塞入n个数,2操作却只能拿掉1个数。。。)
于是乎。。
剩下的就只有模拟了

代码:

#include<bits/stdc++.h>
using namespace std;
#define M 100005
#define S 22
vector<int>edge[M];
int k,a[M],mp[M],mark[M],fa[M][S];
priority_queue<int>Q;
void dfs(int x,int pre){
    for(int i=0;i<edge[x].size();i++){
        int y=edge[x][i];
        if(y==pre)continue;
        dfs(y,x);
    }a[++k]=x;//线映射树
    mp[x]=k;//树映射线
    for(int i=0;i<edge[x].size();i++){
        int y=edge[x][i];
        if(y==pre)continue;
        fa[mp[y]][0]=mp[x];//跳一步到达father
    }
}
int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d %d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }for(int i=1;i<=n;i++)sort(edge[i].begin(),edge[i].end());//先去小序号的结点
    dfs(1,0);
    for(int i=1;(1<<i)<=n;i++)
        for(int j=1;j<=n;j++)//倍增
            if(fa[j][i-1]&&fa[fa[j][i-1]][i-1])fa[j][i]=fa[fa[j][i-1]][i-1];
    for(int i=1;i<=n;i++)Q.push(-i);//大顶堆->小顶堆
    while(m--){
        int f,x;
        scanf("%d %d",&f,&x);
        if(f==1){
            int id;
            while(x--){
                id=-Q.top();Q.pop();
                mark[id]=1;//id被占用
            }printf("%d\n",a[id]);
        }else{
            int ans=0;x=mp[x];
            for(int i=S-1;i>=0;i--)//倍增跳至x点已被占用的最大父亲
                if(fa[x][i]&&mark[fa[x][i]]){
                    ans+=1<<i;
                    x=fa[x][i];
                }
            mark[x]=0;Q.push(-x);//这个点空闲
            printf("%d\n",ans);
        }
    }return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值