19.11.17ACM集训补题

CodeForces - 1250A

这道题会根据a数组直接对a[i]进行操作,可能与前面交换,可能位置不变。

然后要你求出每个值最小和最大的位置


都说做题前要分析,但是我看了几遍还是没想到用什么算法简单

但其实是我不会分析

因为模拟也是种做法,并且按题意模拟的话,也不过是遍历a数组的操作,仅仅O(m)的复杂度而已。这就是想复杂了,要把模拟加入考虑,并且判断复杂度能不能过

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int maxn=1e5+7;
int n,m,a[maxn*4],l[maxn],h[maxn],num[maxn],pos[maxn];
///数字i的位置是pos[i],位置i的数字是num[i]
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        l[i]=h[i]=num[i]=pos[i]=i;
    for(int i=1;i<=m;i++){
        int psi=pos[a[i]];
        if(psi==1)continue;
        int t=num[psi-1];///t是前一个位置的数字
        pos[a[i]]=psi-1;
        pos[t]=psi;
        num[psi]=t;
        num[psi-1]=a[i];
        l[a[i]]=min(l[a[i]],psi-1);
        h[t]=max(h[t],psi);
    }
    for(int i=1;i<=n;i++)
        cout<<l[i]<<' '<<h[i]<<endl;
    return 0;
}
View Code

CodeForces - 1250L

这道题明显是个二分啊

先确定屋子的大小,然后尽量把a或c单独放一个屋子

如果有一个屋子最后还是同时放了a和c,那就失败

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int ce,a,b,c;
bool check(int mid){
    if(a>mid&&c>mid)return false;
    if(a<=mid&&c<=mid)return true;
    if(a>mid)
        return a<=mid*2;
    if(c>mid)
        return c<=mid*2;
}
int main(){
    scanf("%d",&ce);
    while(ce--){
        scanf("%d%d%d",&a,&b,&c);
        int l=(a+b+c-1)/3+1,r=max(a,max(b,c)),ans;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

总结我一个常见错误——就是我常用++,--,+=,-=,然后就会在多次判断的题里WA

 
这道题题意是选择车厢大小s,保证s每次至少能装一个队最多装两个队,然后运r次运完
得到的花费s*r最小

我前面做了一道二分,然后看这道题也像二分
不过明显这道题判断条件不是成功与否,而是花费多少
所以我果断地选择了三分答案,让LR逼近花费少的答案,然后WA
为什么会WA呢,因为我即使能通过知道s算出需要的花费,但是没考虑到花费并不与s成单调关系
比如3 2 3 用3的车厢运3次花费9,而4的车厢运3次花费12,5的车厢运两次花费10
所以三分是不行的(虽然也过了20多个样例)
然后看了题解,这道题应该是贪心
贪心策略是尽量把人数多的队和人数少的队放一起,使每个车厢的人差不多,然后把人数特别多的队单独放一个车厢。理想状态是有多少人就花费多少
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxk=8007;
const int inf=0x7fffffff;
int n,m,k,pep[maxk],maxroom;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
        scanf("%d",&m);
        pep[m]++;
    }
    sort(pep+1,pep+1+k);
    if(k==1)maxroom=pep[1];
    else{
        for(int i=1,j=k;j-i>=1;i++,j--)
            maxroom=max(maxroom,pep[i]+pep[j]);
    }
    int ans=inf;
    for(int i=pep[k];i<=maxroom;i++){
        int l=1,h=k,r=0;
        while(l<=h){
            if(pep[l]+pep[h]<=i)
                l++,h--;
            else h--;
            r++;
        }
        ans=min(ans,i*r);
    }
    printf("%d\n",ans);
    return 0;
}
View Code

CodeForces - 1250J

给出1到N的身高的人的个数,想排成k排,每排的身高最低最高差距不大于1,每排人数一样,问最多参与排队的人的个数


我就是做了这道和上上道都是二分,然后才以为是专题,然后用二分做上道题的
这道题用二分,先确定一排的人数,然后看这样排能不能排,然后得到k排条件下,一排最多的人数
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
typedef long long LL;
const int maxn=3e4+7;
int ce,n;
LL k,c[maxn],sum,l,r,ans;
bool check(LL mid){
    LL numk=0,lef=0;
    for(int i=1;i<=n;i++){
        LL t=(lef+c[i])/mid;
        numk+=t;
        if(numk>=k)return true;
        lef=lef>=t*mid?c[i]:c[i]+lef-t*mid;
    }
    return false;
}
int main(){
    scanf("%d",&ce);
    while(ce--){
        scanf("%d%lld",&n,&k);
        sum=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&c[i]);
            sum+=c[i];
        }
        l=1,r=sum/k,ans=-1;
        while(l<=r){
            LL mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        printf("%lld\n",ans!=-1?ans*k:0);
    }
    return 0;
}
View Code

CodeForces - 1250N

个人认为是道综合性很强的图论。虽然看别人题解说是简单图论,可是代码还是很老实的...

要对构造图很了解才行

题解

CodeForces - 1250C

这个线段树就更没看懂了,先mark一下吧

题解

猜你喜欢

转载自www.cnblogs.com/helman/p/11893989.html
今日推荐