zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",ans!=-1?ans*k:0);
        }
        return 0;
    }
    View Code

    CodeForces - 1250N

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

    要对构造图很了解才行

    题解

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int>P;
    const int maxn=1e5+7;
    struct edge{
        int to,next;
    }e[maxn*2];
    vector<int>v;
    int ce,n,len,tot,sz,head[maxn*2],wire[maxn*2],fr[maxn*2],ed[maxn*2],num[maxn*2];
    P p[maxn];
    bool vis[maxn*2];
    void add(int u,int v){
        e[tot].next=head[u];
        e[tot].to=v;
        head[u]=tot++;
    }
    void dfs(int u,int fa){
        vis[u]=true;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(!vis[v]){
                ed[sz]=v;
                dfs(v,u);
            }
        }
    }
    int main(){
        scanf("%d",&ce);
        while(ce--){
            scanf("%d",&n);
            v.clear();tot=sz=0;
            memset(vis,false,sizeof(vis));
            memset(head,-1,sizeof(head));
            for(int i=1;i<=n;i++){
                scanf("%d%d",&p[i].first,&p[i].second);
                v.push_back(p[i].first);
                v.push_back(p[i].second);
            }
            sort(v.begin(),v.end());
            v.erase(unique(v.begin(),v.end()),v.end());
            len=unique(v.begin(),v.end())-v.begin();
    //        for(int i=0;i<v.size();i++)
    //            cout<<v[i]<<' ';
            for(int i=1;i<=n;i++){
                int id1=lower_bound(v.begin(),v.end(),p[i].first)-v.begin();
                num[id1]=p[i].first;
                int id2=lower_bound(v.begin(),v.end(),p[i].second)-v.begin();
                num[id2]=p[i].second;
                wire[id1]=tot;
                add(id1,id2);
                wire[id2]=tot;
                add(id2,id1);
            }
    //        cout<<num[2]<<endl;
            for(int i=0;i<len;i++)
            if(!vis[i]){
    //            cout<<num[i]<<endl;
    //            cout<<i<<endl;
                sz++;
                fr[sz]=i;
                dfs(i,-1);
            }
            printf("%d
    ",sz-1);
    //        for(int i=1;i<=sz;i++)
    //            cout<<num[fr[i]]<<' ';
    //        cout<<endl;
            for(int i=1;i<sz;i++)
                cout<<wire[ed[i]]/2+1<<' '<<num[ed[i]]<<' '<<num[fr[i+1]]<<endl;
        }
        return 0;
    }
    View Code

    以下是注意:

    1建立双向边的时候,可以设置边的序号是0,1或2,3,这样边的两个结点/2+1就都变成1或2了

    2 1e5的输入,有可能两个点都不一样,结果造成2e5的数据,我就这样WA了好几次

    CodeForces - 1250C

    看了好久终于看懂了,说说自己对题解的理解吧

    首先题意是选择[L,R]的区间,使得所有L<=Li,Ri<=R的计划报酬之和减去(R+1-L)*K最大

    事关区间,我能想到贪心和背包

    如果这里的有时间重叠的计划不能同时做的话,我会去考虑背包,但是不是,所以背包排除

    而如果把计划的开始时间排个序,好像也不能找到贪心策略,毕竟报酬多的计划也可能区间比较长,反而性价比不高

    让人没想到的是选择用线段树


    其实这里算报酬增加的钱是比算区间减去的时间要麻烦的
    因为减的时候每天固定减k,需要考虑的是区间长度
    而加的时候区间固定加p,但是要考虑加在哪个区间
    给这个区间加p,怎么加?用差分,左端点加p,右端点减p?
    其实可以转换一下,直接以每个区间作为处理的最小单位
    就像区间更新减k的时候,我们平时是给线段树减去k*(r+1-l)的
    而如果线段树最底端放的是各个区间的话,就是直接把这个区间减k就行了
    这样原来不知道怎么处理加p,就变成了
    给这个区间加p再减去(r+1-l)个k了
    所以我觉得用线段树的主要目的是处理加p
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<vector>
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn=2e5+7;
    typedef pair<LL,int>pi;
    typedef tuple<LL,int,int>tp;
    int n,l,r;
    LL k,p,lazy[maxn<<2];
    vector<tp>v[maxn];
    vector<int>ans;
    pi st[maxn<<2];
    void pushdown(int rt){
        if(lazy[rt]){
            lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt];
            st[rt<<1].first+=lazy[rt],st[rt<<1|1].first+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void pushup(int rt){
        st[rt]=max(st[rt<<1],st[rt<<1|1]);
    }
    void build(int l,int r,int rt){
        if(l==r){
            st[rt]=make_pair(0LL,l);
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        pushup(rt);
    }
    void updata(int l,int r,int rt,int ll,int rr,LL d){
        if(ll<=l&&r<=rr){
    //    if(l==ll&&r==rr){
            st[rt].first+=d;
            lazy[rt]+=d;
            return;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
    //    if(rr<=mid)updata(l,mid,rt<<1,ll,rr,d);
    //    else if(ll>mid)updata(mid+1,r,rt<<1|1,ll,rr,d);
    //    else {
    //        updata(l,mid,rt<<1,ll,mid,d);
    //        updata(mid+1,r,rt<<1|1,mid+1,rr,d);
    //    }
        if(ll<=mid)updata(l,mid,rt<<1,ll,rr,d);
        if(rr>mid)updata(mid+1,r,rt<<1|1,ll,rr,d);
        pushup(rt);
    }
    int main(){
        scanf("%d%lld",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d%d%lld",&l,&r,&p);
            v[r].push_back(make_tuple(p,l,i));
        }
        p=0;
        build(1,maxn,1);
        for(int i=1;i<maxn;i++){
            updata(1,maxn,1,1,i,-k);
            for(tp P:v[i])updata(1,maxn,1,1,get<1>(P),get<0>(P));
            if(st[1].first>p){
                p=st[1].first;
                r=i,l=st[1].second;
            }
        }
        if(p<=0)printf("0
    ");
        else{
            for(int i=l;i<=r;i++)
                for(tp P:v[i])
                if(get<1>(P)>=l)ans.push_back(get<2>(P));
            printf("%lld %d %d %d
    ",p,l,r,(int)ans.size());
            for(int id:ans)printf("%d ",id);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    使用EntityFramework之Code First开发与MySql数据库问题一例
    使用Emit创建DBContext对象
    一个实用的Metro滚屏效果示例
    合理使用EntityFramework数据验证的异常错误提示信息
    Metro app中使用内存流压缩和解压
    64位的windows操作系统上运行32位asp.net web应用程序的时候,出现试图加载格式不正确的程序的解决方法
    VSTO中自定义XML功能区实现Ribbon控件互相更新
    基于任务并行库实现多线程下载示例
    Android系统刷机成功后网络信号显示“无服务”修正
    Solution Explorer中显示依赖文件和链接文件
  • 原文地址:https://www.cnblogs.com/helman/p/11893989.html
Copyright © 2011-2022 走看看