Codeforces Round #345 (Div. 2)

本人太弱只能打div2,当时只做出来三道题而且巨慢,于是掉了rating = = 后期补全了剩下两题的代码,还是发到博客上来看看吧…

A. Joysticks

题意简述:有两个电池有一些电量,每个单位时间充电会给它加1的电量,不充会减少2的电量,若电量为1你必须给它充电,问你最多能使它们保持有电多久?
数据范围: a,b100
题解:贪心考虑每个单位时间都选电量最好的充就好了,然而有个弥天巨坑是两个电池都为1时要输出0……整个机房的人都被坑了,我也WA了三次,还是某老司机看群才知道的,浪费好多时间简直剧毒A题= =

Code

#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

int a,b;

int main(){
    scanf("%d%d",&a,&b);
    if(a==1 && b==1){
        printf("%d",0);
        return 0;   
    }
    for(int i=1;;i++){
        if(a<b) a++,b-=2;
        else b++,a-=2;
        if(a<=0 || b<=0){
            printf("%d",i);
            return 0;
        }
    }
    return 0;   
}

B. Beautiful Paintings

题意简述:给你一个数列a包含n个数,每两个相邻的数之间若满足 ai+1<ai ,则会产生一点贡献,你可以任意排列这n个数来使得得到最大的贡献值。
数据范围: n1000
题解:同样考虑贪心,把数列分成k段递增的子列了,且k尽可能相同大小,只要让相同的数隔得远一点就好了,用桶暴力统计就可以了……数据范围兹瓷 n2 ,然而机房大神zyj表示用n减去最多的出现次数就好了,可能还要讨论一下有多个数出现最多次?反正我太弱只能暴力了。

Code

#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 1000+5

using namespace std;

int a[maxn],pd[maxn],t[maxn];
int n,ans,tmp,last;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),t[a[i]]++;
    sort(a+1,a+1+n);
    bool flag=0;
    for(;!flag;){
        flag=1; tmp=0;
        for(int i=1;i<=1000;i++)
            if(t[i]) t[i]--,tmp++,flag=0;
        if(!flag) ans+=tmp-1;
    }
    printf("%d",ans);
    return 0;   
}

C. Watchmen

题意简述:平面直角坐标系上给你n个点,求曼哈顿距离等于欧氏距离的点对的个数
数据范围: (n200000,|xi|,|yi|109)
题解:就是统计有多少个点对同一行或者一列上,可以直接对横坐标排序一次,扫一遍统计,再对纵坐标排序一次,扫一遍统计。同一行同一列的乘上一个组合数 (k2) 。注意有一些点坐标相同,在第一次统计的时候顺便统计出来,最后删去即可。
(PS:这个重复点的小bug我调了好久= = 简直sb了,比别人晚交十多分钟,掉了200+分)
(PSS:代码好丑)

Code

#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 200000+5

using namespace std;

struct Point{
    int x,y;    
}a[maxn];

long long C[maxn][3];
long long ans,ans2;
int n,tmp,tmp2;

bool cmp1(const Point &s,const Point &b){
    if(s.x==b.x) return s.y<b.y;
    return s.x<b.x;
}

bool cmp2(const Point &s,const Point &b){
    if(s.y==b.y) return s.x<b.x;
    return s.y<b.y;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i].x,&a[i].y);  
    for(int i=0;i<=n;i++)
        C[i][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=min(2,i);j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    sort(a+1,a+1+n,cmp1);
    a[0].y=a[0].x=-23333333; tmp=1; tmp2=1;
    for(int i=1;i<=n;i++){
        if(a[i].x==a[i-1].x && a[i].y==a[i-1].y) tmp2++;
        else ans2+=C[tmp2][2],tmp2=1;
        if(a[i].x==a[i-1].x) tmp++;
        else ans+=C[tmp][2],tmp=1;
    }
    if(tmp>=2) ans+=C[tmp][2];  tmp=1;
    if(tmp2>=2) ans2+=C[tmp2][2];
    sort(a+1,a+1+n,cmp2);
    for(int i=1;i<=n;i++){
        if(a[i].y==a[i-1].y) tmp++;
        else ans+=C[tmp][2],tmp=1;
    }
    if(tmp>=2) ans+=C[tmp][2];
    printf("%lld",ans-ans2);
    return 0;   
}

D. Image Preview

题意简述:你有n张摆成环形的图片,有些是横着摆的,有些是竖着摆的,你的手机是保持竖着的(没看懂英文瞎口胡的,意思差不多就好23333),你从第一张图片开始看,每张图片若与你手机方向不同,你需要花 b+1 的时间来观看它,否则你只需要花 1 的时间看,同时相邻的图片之间移动需要花费a 的时间,当你看完某张图片后,再看它不需要时间(你可以来回看,只不过移动要时间),你现在有 T 的时间,问你最多能看多少图片。
数据范围:(1n5105,1a,b1000,1T109)
题解:把环拓展一下成链,显然我只能看包括一号点的中间一段图片,这样就可以枚举左端点,然后二分右端点了,对于已经固定的两个端点,显然只有两种移动方式,先到左边和先到右边之后再回来,取最小值来完成对二分的判定。当然你也可以直接用两个指针扫过去就好了,不过打的时候我两个指针扫的一些细节没想清,没打完这题就爆炸了= =。

Code

#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 1000000+5

using namespace std;

char s[maxn];
int step[maxn];
int n,a,b,T,N,ans; 

bool check(int L,int R){
    if(R<n+1) return true;
    int tmp1=0,tmp2=0;
    tmp1=tmp2=step[R]-step[L-1];
    tmp1+=(n-L+1)*a; tmp1+=(R-L)*a;
    tmp2+=(R-n-1)*a; tmp2+=(R-L)*a;
    return min(tmp1,tmp2)<=T;
}

int div2(int L,int R){
    int l=n+1,r=R,res=l;
    if(!check(L,n+1)) return -1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(L,mid)) l=mid+1,res=mid;
        else r=mid-1;
    }
    return res-L+1;
}

int main(){
    scanf("%d%d%d%d",&n,&a,&b,&T);
    scanf("%s",s+1); N=n*2;
    for(int i=n+1;i<=N;i++) s[i]=s[i-n];
    for(int i=1;i<=N;i++)
        if(s[i]=='w') step[i]=step[i-1]+b+1;
        else step[i]=step[i-1]+1;
    for(int i=1;i<=n+1;i++){
        int tmp=div2(i,N);
        if(tmp>0) ans=max(ans,tmp);
    }
    printf("%d",min(ans,n));
    return 0;   
}

E. Table Compression

题意简述:给你一个 nm 的矩阵,要求对里面的元素进行离散化,保证每一行和每一列之间的元素大小关系不变,同时要求最后的矩阵最大值最小,输出离散化后的矩阵。
数据范围: nm106,1ai,j109
题解:整个矩阵满足一个拓扑关系,直接对每一行排序然后添边,相同的元素并查集合并,之后拓扑排序+DP就OK了。然后机房大神TB表示不用添边也可以,本蒟蒻还没仔细想怎么实现= =

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define maxn 2000000+5

using namespace std;

struct Node{
    int val,pos;
}tmp[maxn]; 

struct Graph{
    int u[maxn],v[maxn],head[maxn],nxt[maxn];
    int ind;
    Graph(){ ind=0; memset(head,-1,sizeof(head)); }
    void addedge(int x,int y){
        u[ind]=x; v[ind]=y; nxt[ind]=head[x]; head[x]=ind++;
    }
}G,Gn;

int mat[maxn],d[maxn],du[maxn],stack[maxn],f[maxn];
int n,m,N,cnt,top;

inline int in(){
    int x=0;
    char ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;   
}

bool cmp(const Node &s,const Node &b){
    if(s.val==b.val) return s.pos<b.pos;
    return s.val<b.val;
}

int find(int x){
    if(d[x]==x) return x;
    return d[x]=find(d[x]);
}

void unite(int x,int y){
    int a=find(x),b=find(y);
    if(a==b) return;
    d[a]=b;
}

void build(int lim){
    sort(tmp+1,tmp+1+lim,cmp);
    for(int i=2;i<=lim;i++)
        if(tmp[i-1].val==tmp[i].val) unite(tmp[i-1].pos,tmp[i].pos);
        else G.addedge(tmp[i-1].pos,tmp[i].pos);
}

void Toposort(){
    for(int i=1;i<=N;i++)
        if(find(i)==i && !du[i]) stack[++top]=i,f[i]=1;
    while(top){
        int x=stack[top--];
        for(int i=Gn.head[x];i!=-1;i=Gn.nxt[i]){
            int v=Gn.v[i];
            if(!(--du[v])) stack[++top]=v;
            f[v]=max(f[v],f[x]+1);
        }
    }
}

int main(){
    scanf("%d%d",&n,&m); N=n*m;
    for(int i=1;i<=N;i++)
        mat[i]=in(),d[i]=i;
    for(int i=1;i<=N;i++){
        tmp[++cnt].val=mat[i]; tmp[cnt].pos=i;
        if(cnt==m){ cnt=0; build(m); }
    }
    for(int i=1;i<=m;i++){
        for(int j=i;j<=N;j+=m) 
            tmp[++cnt].val=mat[j],tmp[cnt].pos=j;
        cnt=0; build(n);
    }
    for(int i=1;i<=N;i++)
        for(int j=G.head[i];j!=-1;j=G.nxt[j])
            if(find(G.u[j])!=find(G.v[j])){
                Gn.addedge(d[G.u[j]],d[G.v[j]]);
                du[d[G.v[j]]]++;
            }
    Toposort();
    for(int i=1;i<=N;i++){
        printf("%d",f[d[i]]);
        if(i%m==0) puts("");    
        else putchar(' ');
    }
    return 0;   
}

Sad Story

因为A,C题一开始爆炸,D题开始的晚就没打完,导致机房排最后= = 然而之后结果一出来FST了一堆人……排名就往前面翻了一点。。。好吧然而这次题这么水,只切三道果然是掉rating了。。。。悲伤啊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值