Codeforces Round #433 (Div. 2) 总结

这场么…又一次打炸掉了..(不知道为啥这场他们的手速都这么快,我在1:47用一个保证fst的算法过了D题pp结果也才400名)..最后加上D题fst…排名可想而知。(惨啊!qwq)
最终排名:rank813,rating-=41,目前rating:1764(好像这几场下来离紫名越来越远了啊有木有…)
P.S. 这场打完我还有一个教训…虽然打前两题手速要快,但是..太快就会GG啊。。

A. Fraction

题意:给你一个正整数n,问你能求得的最大的真分数且是既约分数且分子与分母的和为n的分数是多少,输出分子和分母。
思路&&题解:这一题我一上来看了1分30时我就打完了,然后交上去WA掉了..白白少了50分。最后再仔细看一遍题,发现我没有看到既约分数..最后加个GCD判一下就好了..

代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n;
inline int gcd(int a,int b) {
    return b?gcd(b,a%b):a;
}
int main() {
    cin>>n;
    if(n%2==0) {
        int a=n/2-1,b=n/2+1;
        while(gcd(a,b)!=1) {
            a--;
            b++;
        }
        cout<<a<<" "<<b<<endl;
    }
    else {
        int a=n/2,b=n/2+1;
        while(gcd(a,b)!=1) {
            a--;
            b++;
        }
        cout<<a<<" "<<b<<endl;
    }
    return 0;
} 

B. Maxim Buys an Apartment

题意: Maxim想在一条街上买一幢公寓,这条街上有n幢公寓标号从左到右为1~n,Maxim定义一幢公寓为“好”的,只要和它相邻的公寓中至少有一幢已经有人住了。现在Maxim知道其中有k幢已经被人买走了且有人住了,但他不知道现在住了人的公寓的标号是多少,现在让你求最少有多少公寓是“好”的和最多有多少公寓是“好”的。
思路&&题解:首先,显然的是当k==n或k==0的时候,答案一定是0 0(显然)。否则最少的情况一定是k幢公寓从编号1开始是连着被买的,这样只有一幢公寓是好的。现在我们考虑最大多少幢,我们可以发现就从编号2开始每隔两个放一个,(即2,5,8….)(可以证明这是最优的),那么答案就是min(n-k,2*k)..我刚开始没特判k==0 WA了..还有一次瞎写了个算法GG了..

代码如下:

#include<bits/stdc++.h>
using namespace std;
long long n,k;
int main() {
    cin>>n>>k;
    if(n==k||k==0) {
        cout<<"0 0"<<endl;
        return 0;
    }
    cout<<"1 ";
    if(k<=n/3)
        cout<<k*2<<endl;
    else
        cout<<min(n-k,k*2)<<endl;
    return 0;
}

C. Planning

题意:飞机场原定计划从第一分钟开始每分钟起飞一架飞机,但是由于某些原因导致前k分钟无法起飞飞机。给出每架飞机每延误一分钟的损失 costi ,问所有飞机都起飞后的最小损失为多少。
思路&&题解:这题只要贪心一下就好了。用一个堆(其实优先队列就行了233)..然后根据cost排列,一个一个赋值下去就行了2333..(其实挺好写的)

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=300050;
ll n,k;
struct node{
    ll num;
    int id;
    bool operator < (const node &rhs) const {
        return num<rhs.num;
    }
};
priority_queue<node> a;
ll ans[maxn];
ll sum=0,x[maxn];
bool vis[maxn<<1];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;i++) {
        cin>>x[i];
        if(i<=k+1)
            a.push((node){x[i],i});
    }
    for(int pos=k+1;pos<=k+n;pos++) {
        node now=a.top();
        a.pop();
        ans[now.id]=pos;
        sum+=now.num*(pos-now.id);
        if(pos<n)
            a.push((node){x[pos+1],pos+1});
    }
    cout<<sum<<endl;
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<" ";
    cout<<endl;
    return 0;
}

D. Jury Meeting

题解:给你m个航班,然后使得n个特派员在0号城市开会,最少要让这n个特派员一起在这里待上k天,然后每个航班的航线、时间和花费已知,问你满足题意的最小花费。如果不满足,则输出-1。
思路&&题解:这题看起来有点难..实际上想通了挺简单的。只是当时我来不及打了,然后随便瞎糊了个暴力,没想到过pp了,(虽然最后不用想也知道肯定fst..qwq)。实际上这题就是贪心。只要将航班按时间排序,然后从前往后维护出发航班前缀最小值,然后从后往前维护返回航班的后缀最小值,最后枚举一下从哪天开始n个特派员都在0号城市就行了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200050;
struct Edge{
    ll d,from,to,val;
}a[maxn];
ll n,m,k,sum,tot,vis[maxn],l[maxn],r[maxn],ans=1e18;
inline ll cmp(Edge a,Edge b) {
    return a.d<b.d;
}
inline void fastIO() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
} 
inline void init() {
    memset(vis,0,sizeof vis);
    memset(l,-1,sizeof l);
    memset(r,-1,sizeof r);
}
int main() {
    fastIO();
    cin>>n>>m>>k;
    init();
    for(int i=1;i<=m;i++)
        cin>>a[i].d>>a[i].from>>a[i].to>>a[i].val;
    sort(a+1,a+m+1,cmp);
    sum=tot=0;
    for(int i=1;i<=m;i++) {
//      cout<<i<<" "<<sum<<" "<<tot<<endl;
        if(a[i].from==0)
            continue;
        if(vis[a[i].from]==0) {
            vis[a[i].from]=a[i].val;
            sum+=a[i].val;
            tot++;
        }
        else {
            if(vis[a[i].from]>a[i].val) {
                sum-=vis[a[i].from];
                vis[a[i].from]=a[i].val;
                sum+=a[i].val;
            }
        }
        if(tot==n)
            l[i]=sum;
    }
    sum=tot=0;
    memset(vis,0,sizeof vis);
    for(int i=m;i>=1;i--) {
        if(a[i].to==0)
            continue;
        if(vis[a[i].to]==0) {
            sum+=a[i].val;
            tot++;
            vis[a[i].to]=a[i].val;
        }
        else {
            if(vis[a[i].to]>a[i].val) {
                sum-=vis[a[i].to];
                sum+=a[i].val;
                vis[a[i].to]=a[i].val;
            }
        }
        if(tot==n)
            r[i]=sum;
    }
    for(int i=m;i>=1;i--) {
        if(r[i]==-1)
            continue;
        else {
            if(r[i+1]==-1)
                continue;
            r[i]=min(r[i],r[i+1]);
        }
    }
    for(int i=1;i<=m;i++) {
//      cout<<i<<" "<<l[i]<<endl;
        if(l[i]==-1)
            continue;
        int pos=-1,lim=a[i].d+k+1,lef=i+1,rig=m,mid;
        while(lef<=rig) {
            mid=(lef+rig)>>1;
            if(a[mid].d>=lim) {
                rig=mid-1;
                pos=mid;
            }
            else
                lef=mid+1;
        }
        if(pos==-1)
            continue;
        else if(r[pos]!=-1)
            ans=min(ans,r[pos]+l[i]);
    }
    cout<<(ans==1e18?-1:ans)<<endl;
    return 0;
}

E. Boredom

题意:一个n*n的网格图,有n个标记,每列只有一个标记,定义美丽的矩形为以两个标记所在位置构成的矩形(对角线的两个角)。q次询问,每次询问给你一个矩形,问有多少个美丽的矩形与该矩形相交。
思路&&题解:这题可以用树状数组+离线回答做。首先我们可以知道,如果一个区域内有n个标记,那么这n个标记可以组成n*(n-1)/2个矩形。我们可以将大矩形根据询问的范围将它分成九块,用一维树状数组维护四个角的点数,然后将询问先从左到右排序一遍,更新答案;然后将询问从右到左排序一遍继续更新答案,最后再弄一下就好了(详情请见代码)

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=200050;
int n,m,l,d,u,r,a[maxn];
ll ans[maxn];
struct treearray {
    ll val[maxn];
    inline int lowbit(int x){return x&(-x);};
    inline void clear(){memset(val,0,sizeof val);}
    inline void add(int x){for(int i=x;i<=n;i+=lowbit(i))val[i]++;}
    inline ll query(int x){ll res=0;for(int i=x;i>=1;i-=lowbit(i))res+=val[i];return res;}
}tx,ty;
struct que {
    int l,d,r,u,id;
    que(int l=0,int d=0,int r=0,int u=0,int id=0):l(l),d(d),r(r),u(u),id(id){}
}re[maxn];
inline bool cmP(que a,que b){return a.l<b.l;}
inline bool Cmp(que a,que b){return a.r>b.r;}
inline ll f(ll x){return 1LL*(x-1)*x/2LL;}
inline void fastIO(){ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
int main() {
    fastIO();
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>l>>d>>r>>u,re[i]=que(l,d,r,u,i);
    sort(re+1,re+m+1,cmP);
    int pos=1;
    for(int i=1;i<=n&&pos<=m;i++) {
        while(re[pos].l==i&&pos<=m)ans[re[pos].id]+=f(tx.query(re[pos].d-1))+f(ty.query(n-re[pos].u)),pos++;
        tx.add(a[i]),ty.add(n-a[i]+1);
    }
    tx.clear(),ty.clear();
    sort(re+1,re+m+1,Cmp);
    pos=1;
    for(int i=n;i>=1&&pos<=m;i--) {
        while(re[pos].r==i&&pos<=m)ans[re[pos].id]+=f(tx.query(re[pos].d-1))+f(ty.query(n-re[pos].u)),pos++;
        tx.add(a[i]),ty.add(n-a[i]+1);
    }
    for(int i=1;i<=m;i++)ans[re[i].id]+=f(n),ans[re[i].id]-=f(re[i].l-1)+f(n-re[i].r)+f(n-re[i].u)+f(re[i].d-1);
    for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值