zoukankan      html  css  js  c++  java
  • Codeforces Round #380 (Div. 1, Rated, Based on Technocup 2017

    http://codeforces.com/contest/737

    A: 

    题目大意: 有n辆车,每辆车有一个价钱ci和油箱容量vi.在x轴上,起点为0,终点为s,中途有k个加油站,坐标分别是pi,到每个加油站都可以加满油。 每辆车有2种模式,加速模式花1分钟和2个单位的油前进1个单位,正常模式花2分钟和1个单位的油前进1个单位。  要求选一辆最便宜的车,使得开这辆车从起点到终点的时间小于等于t.从起点出发的时候油是满的。

    n,k<=105.  vi,pi,s<=109

    思路:

    首先由于每次都可以加满油,可以对每一段路分开考虑。 假设某一段长度为len个单位,设有x个单位的路是用加速模式的,那么耗时t=x+2*(len-x)=2*len-x ;

    显然x越大t越小。    但是还要满足2个条件: x<=len ,  耗油量 2x+(len-x)=x+len<=v.   要把这两个条件合并起来,只要分两类讨论。

    当len<=0.5v的时候,  x<=len;     当x=len的时候t取最小值len;

    当len>  0.5v的时候,  x<=v-len;  当x=v-len的时候t取最小值3*len-v;

    所以只要把所有的len按长度排个序,  枚举车辆i, len<=0.5vi的可以一起算, len>0.5vi的一起算。 二分一下分界点就好。 时间复杂度O(nlogn).

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cstdlib>
    #include <set>
    using namespace std;
    
    #define X first
    #define Y second
    #define Mod 1000000007
    #define N 200010
    typedef long long ll;
    typedef pair<int,int> pii;
    
    inline int Mul(int x,int y){return 1ll*x*y%Mod;}
    inline int Add(int x,int y){return ((x+y)%Mod+Mod)%Mod;}
    
    int n,s,t,k,r;
    int p[N],c[N],v[N],b[N];
    ll sum[N];
    
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
        
        scanf("%d%d%d%d",&n,&k,&s,&t);
        for (int i=1;i<=n;i++) scanf("%d%d",&c[i],&v[i]);
        for (int i=1;i<=k;i++) scanf("%d",&p[i]);
        sort(p+1,p+k+1);
        int maxlen=0;
        for (int i=1;i<=k;i++) maxlen=max(maxlen,p[i]-p[i-1]),b[r++]=p[i]-p[i-1];
        maxlen=max(maxlen,s-p[k]); b[r++]=s-p[k];
        sort(b,b+r);
        sum[0]=b[0];
        for (int i=1;i<r;i++) sum[i]=b[i]+sum[i-1];
        int ans=2e9;
            
        for (int i=1;i<=n;i++)
        {
            if (v[i]-maxlen<0) continue;
            
            int pos=lower_bound(b,b+r,v[i]*0.5)-b;
        
            if (pos==r || 2*b[pos]>v[i]) pos--;
            ll tmp=sum[pos]+3*(sum[r-1]-sum[pos])-1ll*v[i]*(r-pos-1);
            if (tmp<=t && ans>c[i]) ans=c[i];
    
        }
        if (ans==2e9) ans=-1;
        printf("%d
    ",ans);
    
        return 0;
    }
    View Code

    B:

    题目大意:  有n个位置排成一排,在上面放了a个长度为b的互不相交的方块。选一些格子射击,一开始已经射了k次(给了一个01字符串1表示被射过),但是都没射中任何一个方块。现在要再选最少的格子射击,使得不管怎么摆这些方块,至少有一个方块被射到。           n<=105

    思路:

    1.假设我们选好了一些位置来射击,把射击的位置标记为1,如果不管怎么摆这些方块,至少有一个方块被射到 等价于只在0的位置上放方块,最多能放的方块数<a.

    2.对于某一块连续的0,假设长度为len,那么这一段最多能放$frac{len}{b}$ 个方块。 因此如果我们让len减少b,能放的方块就少了一个。所以这样构造:在连续的0上每隔b个单位就射击一次,这样每射击一次能放得方块数就减少1,直到减少到a-1.  显然这样射击的次数是最少的。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cstdlib>
    #include <set>
    using namespace std;
    
    #define X first
    #define Y second
    #define Mod 1000000007
    #define N 200010
    typedef long long ll;
    typedef pair<int,int> pii;
    
    int n,a,b,k,t;
    int l[N],r[N],len[N],ans[N];
    char s[N];
    
    
    inline int f(int x){return x/b;}
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
        
        scanf("%d%d%d%d",&n,&a,&b,&k);
        scanf("%s",s+1); s[n+1]='1';
        for (int i=1,pre=0;i<=n+1;i++) 
        {
            if (s[i]=='1')
            {
                t++;
                l[t]=pre+1;
                r[t]=i-1;
                len[t]=i-pre-1;
                pre=i;
            }
        }
        
        int sum=0;
        for (int i=1;i<=t;i++) sum+=f(len[i]);
    
        for (int i=1;i<=t;i++)
        {
            int x=l[i]+b-1;
            while (x<=r[i] && sum>=a) ans[++ans[0]]=x,x+=b,sum--;
        }
        printf("%d
    ",ans[0]);
        for (int i=1;i<=ans[0];i++) printf("%d%c",ans[i],i==ans[0]? '
    ':' ');
        
        return 0;
    }
    View Code

    C:

    题目大意:有n个点,以s为根构成一棵树,  每个点有ai个祖先(包括父亲), 要求修改最少的ai,使得存在这样的一棵树。

    思路:

    1. ai其实就是节点的深度。 可以发现,假设一棵树最大深度是maxdep,那么肯定存在深度为0,1,2...maxdep的节点,也就是说深度是连续的。

    2. 节点s的ai必须是0. 如果不是,必须把它修改成0. 之后就可以不管s了。

    3. 考虑枚举最大深度, 先统计一下c[k],表示ai=k的个数。 假设现在枚举到最大深度为d, 深度0-d中有cnt个不存在,  ai>d的这些点肯定是需要修改的,既然要修改,不如把它用来填补那cnt个空位。 另外ai=0的点肯定也是要修改的。  具体实现看代码。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cstdlib>
    #include <set>
    #include <queue>
    using namespace std;
    
    #define X first
    #define Y second
    #define Mod 1000000007
    #define N 200110
    #define M 200110
    typedef long long ll;
    typedef pair<int,int> pii;
    
    int n,s,ans,t;
    int a[N],c[N],sum[N];
    
    int main()
    {
        //freopen("in.in","r",stdin);
        //freopen("out.out","w",stdout);
    
        int cnt=0;  ans=1e9; 
        scanf("%d%d",&n,&s);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        if (a[s]!=0) t=1;  a[s]=n+1;
        if (n==1) {printf("%d
    ",t); return 0;}
        for (int i=1;i<=n;i++) c[a[i]]++;
        for (int i=n-1;i>=0;i--) sum[i]=sum[i+1]+c[i];
        
        for (int i=1;i<n;i++)
        {
            if (!c[i]) cnt++;
            if (sum[i+1]+c[0]>=cnt) ans=min(ans,sum[i+1]+c[0]);
            else ans=min(ans,cnt);
        }
        printf("%d
    ",ans+t);
    
        return 0;
    }
    View Code

    比赛的时候只会ABC, 争取近几天把后面的题补上。

  • 相关阅读:
    解决:npm中 下载速度慢 和(无法将“nrm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确, 然后再试一次)。
    maven(一) maven到底是个啥玩意~
    Luogu3959 NOIP2017宝藏(状压dp)
    Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)
    Luogu3952 NOIP2017时间复杂度
    BZOJ4753 JSOI2016最佳团体(分数规划+树形dp)
    BZOJ1975 SDOI2010魔法猪学院(启发式搜索+最短路+堆)
    BZOJ4105 THUSC2015平方运算(线段树)
    BZOJ5109 CodePlus 2017大吉大利,晚上吃鸡!(最短路+拓扑排序+bitset)
    Luogu3731 HAOI2017新型城市化(二分图匹配+强连通分量)
  • 原文地址:https://www.cnblogs.com/vb4896/p/6083557.html
Copyright © 2011-2022 走看看