Codeforces Round #345 (Div. 2)

居然有场中国下午场次的CF,猴开心啊,于是就报名了...

结果被虐傻了...果然不能太激动啊...

A题题目翻译挂了,少了一个无解的情况,导致机房前20Min没有一个人能过A题...直到后面有高人相助才过,好惨

C题前面都开了long long,只有输出没开I64d,结果就丢了1206分...这什么鬼啊...

D题敲了20min的程序结果挂了,于是删掉重打,打到要结束了才发现题目两个东西弄反了,然后仓促改了一改...呼,然后少改一个地方,然后一堆细节的东西也有问题...表示无奈

A题:两个电池,每秒钟充电的话电量+1,不充电-2,一个<=0,就结束。问最多撑多久?

  感觉贪心就好,就是每次给电量小的+1,结果WA了!好吧,我觉得是我贪心可能有问题,于是我就打了一个大暴力,f[i][j]表示两个电池的状态,WA了!然后我放弃了,我吃饭去了...回来就有人说还有一句话你没看见。

  如果一个电池的电量=1,你必须给它充电。结果1 1输出是0...好坑啊...

O(100^3)大暴力:

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

using namespace std;

int f[210][110][110];

int main(){
#ifndef ONLINE_JUDGE
    freopen("A.in","r",stdin);
#endif

    int a1,a2,t=0;

    scanf("%d%d",&a1,&a2);
    if(a1==1 && a2==1){ printf("0");return 0; }
    f[0][a1][a2]=1;
    f[0][a2][a1]=1;

    for(int i=0;i<=200;i++)
        for(int j=1;j<=102;j++)
            for(int k=1;k<=102;k++)
                if(f[i][j][k]){
                    if(j>2)
                        f[i+1][j-2][k+1]=1;
                    if(k>2)
                        f[i+1][j+1][k-2]=1;
                }
    for(int i=200;i>=0;i--){
        for(int j=1;j<=100;j++)
            for(int k=1;k<=100;k++)
                if(f[i][j][k]){
                    printf("%d",i+1);return 0;
                }
    }

    return 0;
}
View Code

 O(100)的贪心,也可以过。

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

using namespace std;

int main(){
#ifndef ONLINE_JUDGE
    freopen("A.in","r",stdin);
#endif

    int a,b,t=0;

    scanf("%d%d",&a,&b);
    if(a==1 && b==1){
        printf("0");return 0;
    }

    while(a>0 && b>0){
        if(a>b) swap(a,b);
        a++,b-=2;t++;
    }
    
    printf("%d",t);
    return 0;
}
View Code

B题:给你n个序列,然后让你找到一种排列方式,使得ai<ai+1的数目最多。n<=1000,ai<=1000

  好吧,这题当时只想到暴力,唔暴力就是1000*1000,如果这个值有,那么就接在前面的放上这个。最后在得到的序列中暴力算一下。

  然后考完,唔,zyj说这题可以直接n-出现次数最多的元素,好神啊...相当于最小链覆盖=最长反链长度[我怎么会想到导弹拦截去...2333],如果还没理解这个做法的话,就可以脑补一下,发现除了这个出现次数最多的,其它的都可以找到贡献[可以往前也可以往后找贡献]。

  

O(1000^2)大暴力:

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

using namespace std;

const int maxn=1010;

int n,k,ans;
int a[maxn],b[maxn];
int T[maxn];

int main(){
#ifndef ONLINE_JUDGE
    freopen("B.in","r",stdin);
#endif

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),T[a[i]]++;
    for(int i=1;i<=1000;i++)
        for(int j=1;j<=1000;j++){
            if(T[j]) T[j]--,b[++k]=j;
        }
    for(int i=1;i<=k;i++)
        if(b[i]<b[i+1]) ans++;
    printf("%d",ans);
    return 0;
}
View Code

O(1000)的神奇做法

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

using namespace std;

const int maxn=1010;

int n,Max;
int a[maxn],T[maxn];

int main(){
#ifndef ONLINE_JUDGE
    freopen("B.in","r",stdin);
#endif

    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]),T[a[i]]++;
        Max=max(Max,T[a[i]]);
    }
    printf("%d",n-Max);
    return 0;
}
View Code

C题:给你n个点,求有多少点对的欧几里得距离=曼哈顿距离

  这就是问有多少点对在同一行或者同一列,然后*C(这一行/列上点数目,2)就是这一行/列上点对的数量。

  然后还要删掉重合的点的贡献,考试时最后一步没有输出long long导致崩盘。

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

using namespace std;

const int maxn=200010;
typedef long long ll;

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

bool cmp(const Point &A,const Point &B){
    return A.x<B.x;
}

bool cmp2(const Point &A,const Point &B){
    return A.y<B.y;
}

bool cmp3(const Point &A,const Point &B){
    if(A.x!=B.x) return A.x<B.x;
    return A.y<B.y;
}

int n;
ll ans;

int main(){
#ifndef ONLINE_JUDGE
    freopen("C.in","r",stdin);
#endif

    int cnt;

    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&s[i].x,&s[i].y);
    
    sort(s+1,s+n+1,cmp);
    s[n+1].x=s[n+1].y=-1e9;
    for(int i=1;i<=n;i++){
        cnt=1;
        while(s[i].x==s[i+1].x) cnt++,i++;
        ans+=(ll)cnt*(cnt-1)/2;
    }
    sort(s+1,s+n+1,cmp2);
    for(int i=1;i<=n;i++){
        cnt=1;
        while(s[i].y==s[i+1].y) cnt++,i++;
        ans+=(ll)cnt*(cnt-1)/2;
    }
    sort(s+1,s+n+1,cmp3);
    for(int i=1;i<=n;i++){
        cnt=1;
        while(s[i].y==s[i+1].y && s[i].x==s[i+1].x) cnt++,i++;
        ans-=(ll)cnt*(cnt-1)/2;
    }
    
    printf("%I64d",ans);
    return 0;
}
View Code

D题:有n张图片,你可以向左或者向右滑,每次滑需要a秒,看一张横着的图片需要1秒,一张竖着的图片需要1+b秒。如果现在看的图片之前没有看过,那么必须花时间看完它;如果已经看过,可以不花时间在看它上。你一开始在1号图片,问T秒最多看多少图片。

  根据题目信息,这题应该是看一段前缀+一段后缀,那么通过类似贪心的想法来找到最优的值。

  首先贪心找到后缀最远的,同时尝试往前拓展找到这个后缀对应的最远的前缀。然后每次后缀指针往后挪一个,前面的指针跟着动,然后更新答案就行了。而我可以通过比较前缀和后缀的长度来确定一开始翻的方向。

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

using namespace std;

const int maxn=2000010;

typedef long long ll;

int n,l,r,a,b,T,ans;
ll s1[maxn],s2[maxn];
char ch[maxn];

int main(){
#ifndef ONLINE_JUDGE
    freopen("D.in","r",stdin);
#endif
    
    scanf("%d%d%d%d",&n,&a,&b,&T);
    scanf("%s",ch+1);
    
    if(ch[1]=='w') T-=1+b;
    else T-=1;
    
    if(T<0){printf("0");return 0;}
    ans=1;l=1;r=n+1;
    
    for(int i=2;i<=n;i++)
        if(ch[i]=='w') s1[i]=s1[i-1]+1+b;
        else s1[i]=s1[i-1]+1;
    
    for(int i=n;i>=2;i--){
        if(ch[i]=='w') s2[i]=s2[i+1]+1+b;
        else s2[i]=s2[i+1]+1;
        if(T>=s2[i]+(n-i+1)*a) r=i;
    }
    
    if(r==2){printf("%d",n);return 0;}

    for(int i=2;i<=n;i++)
        if(i-1>(n-r+1)){
            if(T>=s1[i]+s2[r]+((n-r+1)<<1)*a+(i-1)*a) l=i;
            else break;
        }
        else{
            if(T>=s1[i]+s2[r]+((i-1)<<1)*a+(n-r+1)*a) l=i;
            else break;
        }
    
    ans=l+(n-r+1);
    
    int t;
    
    while(r<=n){
        r++;
        while(l<n){
            if(l>(n-r+1)){
                if(T>=s1[l+1]+s2[r]+((ll)(n-r+1)<<1)*a+l*a) l++;
                else break;
            }
            else{
                if(T>=s1[l+1]+s2[r]+((ll)l<<1)*a+(n-r+1)*a) l++;
                else break;
            }
        }
        ans=max(ans,l+n-r+1);
    }
    
    
    printf("%d",ans);
    
    return 0;
}
View Code

E题:有个n*m的矩阵要求你将这个矩阵压缩成一个n*m的矩阵,但是要保证每行每列上的权值的相对大小不变。保证相对大小不变的基础上要求最大值最小,求这个压缩后的矩阵。

  我们可以首先将所有元素排一个序,然后每次取出最小的那个,一开始显然标号是1,然后之后的就看和这一行这一列上的关系,如果比这行/列上的元素大,那么就取这行/列上的元素+1;当然对于行列也要取最大值。

  然后因为相等的关系也要保证,所以我们可以用并查集把同一行同一列的相同元素连起来,然后定根的时候,统一取大的作为根,那么查询的时候就会查到这个元素的值了。

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

using namespace std;

const int maxn=1000010;

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

struct Node{
    int x,y,v;
    Node(){}
    Node(int a,int b,int c){x=a,y=b,v=c;}
}s[maxn];

int n,m,tot;
int a[maxn],b[maxn];
int row_mx[maxn],cur_mx[maxn];
int p[maxn];

bool cmp(const Node &A,const Node &B){
    return A.v<B.v;
}

inline int Find(int x){
    int r=x,pre;
    while(r!=p[r]) r=p[r];
    while(x!=r){
        pre=p[x],p[x]=r,x=pre;
    }
    return r;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("E.in","r",stdin);
#endif

    n=in();m=in();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[++tot]=in(),s[tot]=Node(i,j,a[tot]);

    sort(s+1,s+tot+1,cmp);
    
    for(int i=1;i<=tot;i++) p[i]=i;
    for(int i=1;i<=tot;i++){
        int x=s[i].x,y=s[i].y,v=s[i].v;
        int id=(x-1)*m+y,t1=0,t2=0;
        
        int fx=Find(row_mx[x]),fy=Find(cur_mx[y]);
        
        if(v>a[fx]) t1++;
        if(v>a[fy]) t2++;
        
        b[id]=max(b[fx]+t1,b[fy]+t2);
        
        if(v==a[fx]){
            int f=Find(id);
            if(b[f]>b[fx]) p[fx]=f;
            else p[f]=fx;
        }
        
        if(v==a[fy]){
            int f=Find(id);
            if(b[f]>b[fy]) p[fy]=f;
            else p[f]=fy;
        }
        
        row_mx[x]=cur_mx[y]=Find(id);
        
    }
    
    int ip=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            printf("%d ",b[Find(++ip)]);
        putchar('
');
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Robert-Yuan/p/5252267.html