zoukankan      html  css  js  c++  java
  • Codeforces940掉分记

    掉分经过##

    难得这次时间比较好,下午17:35开始。
    本来还很高兴,心想这回肯定不会犯困,没准排名能再上升一些呢,,可惜事与愿违……

    上来a题,光看懂题就花了一些时间。
    然后开始写,结果第一遍CE,第二遍WA……
    出师不利啊!气急败坏,暂时放弃。

    然后看b题。
    惊喜地发现这类似的题我做过,大概是个bfs?嗯dp也行?
    结果发现(n leq 10^9)...真不妙。
    这时来了个有关a题的通知,于是又回去看a题。
    终于找到之前代码中的问题了,过了pretest
    接着c和d题做的还算顺利,1个小时时都过了pretest

    剩一个小时,3道题。
    e和f看完都没什么好的想法,e想用区间dp,f想用线段树,但好像都不太行……
    我又开始慌了……呜哇哇哇啊呀呀呀……

    于是又回去看b题
    发现居然有约1700人过了b的pretest,我猜这肯定不是道难题。
    可能是个鬼畜的贪心?
    嗯,想啊想啊想才终于想出来。
    交了一发,TLE...又一发,WA...第三发,过了。

    此时离比赛结束还有6分钟……
    好了,安心下楼吃晚饭吧。

    于是,就这样,比了最颓的一场cf...
    终测4道题都过了,但由于太慢了,所以排名哗哗向下掉啊…QwQ


    题解##

    A. Points on the line###

    定义一个集合diameter为该集合中的 最大数-最小数
    给定一个n元素的集合与 d,求最少去掉原集合中多少个数后该集合的diameter不超过d
    输入的所有数据皆(leq100)

    想法###

    枚举集合中最后剩下的最大值
    用这个最大值-d得到集合中可剩下的最小值
    找出要去掉的数最少的即可。

    代码###

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 105;
    
    int cnt[N],a[N];
    int n,k;
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        cnt[0]=0;
        for(int i=1;i<=n;i++) cnt[a[i]]++;
        for(int i=1;i<=100;i++) cnt[i]+=cnt[i-1];
        
        if(n==1) { printf("0
    "); return 0;}
        
        int ans=n,f;
        for(int i=n;i>0;i--){
            if(a[i]>k) ans=min(ans,n-cnt[a[i]]+cnt[a[i]-k-1]);        
            else ans=min(ans,n-cnt[a[i]]);
        }
        printf("%d
    ",ans);
        
        return 0; 
    }
    
    

    B. Our Tanya is Crying Out Loud###

    给定n,k,A,B
    设x一开始等于n
    x每减1的代价为A,x每/k(前提为x是k的倍数)的代价为B
    求将x变为1所需的最小代价
    所有输入数据(leq 10^9)

    想法一###

    dp[i]表示从n变到i的最小代价
    dp[i]=min{dp[i+1]+A,dp[i (dots) k]+B}
    但n太大了,时间空间都不行。

    想法二###

    贪心。
    先把x不断减1直到x为k的倍数
    我们发现,若要x变成x/k,直接/k的代价比一点点减还大的话,直接减,一直减到1就行了,否则直接/k
    循环这个过程

    代码###

    贪心

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const int N = 1000000005;
    
    int n,k,A,B;
    
    int main()
    {
        scanf("%d%d%d%d",&n,&k,&A,&B);
        
        ll ans=0;
        int x=n,y;
        while(x>1){
            if(x%k==0) {
                if((ll)B>=((ll)x-x/k)*A) {
                    ans+=(ll)A*(x-1);
                    x=1;
                }
                else {
                    ans+=B;
                    x=x/k;
                }
            }
            else {
                y=max(((x-1)/k)*k,1);
                ans+=((ll)x-y)*A;
                x=y;
            }
        }
        printf("%lld
    ",ans);
        
        return 0;    
    }
    

    C. Phone Numbers###

    给定n,k及一个长度为n的字符串s
    求一个长度为k的字符串t,t由出现在s中的字母组成,且t为满足条件的字典序比s大的第一个字符串。

    想法###

    贪心。
    若k>n,则直接在字符串s后面补上k-n个s中的最小字符即可
    否则从后面的位往前考虑,看是否能变为一个更大的字符。若可以,便变成比它大的第一个字符,后面位都变成s中的最小字符。

    代码###

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    const int N = 100005;
    
    char s[N];
    int n,k,t;
    int cnt[30],num[30];
    int ans[N];
    
    int main()
    {
        char ch;
        scanf("%d%d",&n,&k);
        scanf("%s",s);
        for(int i=0;i<n;i++) cnt[s[i]-'a'+1]=1;
        for(int i=1;i<=26;i++) cnt[i]+=cnt[i-1];
        t=cnt[26];
        for(int i=0;i<n;i++) num[cnt[s[i]-'a'+1]]=s[i]-'a'+1;
        
        if(k<=n){
            for(int i=0;i<k;i++) ans[i]=cnt[s[i]-'a'+1];
            for(int i=k-1;i>=0;i--){
                if(ans[i]+1<=t){
                    ans[i]++;
                    break;                
                }
                ans[i]=1;
            }
            for(int i=0;i<k;i++){
                ch=num[ans[i]]-1+'a';
                cout<<ch;        
            }
        }
        else{
            for(int i=0;i<n;i++) ans[i]=cnt[s[i]-'a'+1];
            for(int i=n;i<k;i++) ans[i]=1;
            for(int i=0;i<k;i++){
                ch=num[ans[i]]-1+'a';
                cout<<ch;        
            }
        }
        
        return 0;    
    }
    
    

    D. Alena And The Heater###

    有两个长度为n的数组a[]和b[],其中b[]由01组成
    b[1]=b[2]=b[3]=b[4]=0
    有l与r
    对于所有 5 ≤ i ≤ n
    当 a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] > r 且 b[i - 1] = b[i - 2] = b[i - 3] = b[i - 4] = 1时 b[i]=0
    当 a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] < l 且 b[i - 1] = b[i - 2] = b[i - 3] = b[i - 4] = 0时 b[i]=1
    否则b[i]=b[i-1]
    给定a[]与b[],求一组合法的l,r

    想法###

    从左到右扫b数组
    一开始令 l=-INF,r=INF
    若存在“00001”的情况,则 l > max{a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] }
    若存在“11110”的情况,则 r < min{a[i], a[i - 1], a[i - 2], a[i - 3], a[i - 4] }
    不断更新l,r即可

    代码###

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    
    using namespace std;
    
    const int N = 100005;
    
    int a[N];
    char b[N];
    int n;
    
    int maxn(int x){
        int ret=-1000000000;
        for(int i=0;i<5;i++)
            ret=max(ret,a[x-i]);
        return ret;    
    }
    int minn(int x){
        return min(a[x],min(a[x-1],min(a[x-2],min(a[x-3],a[x-4]))));  
    }
    
    int main(
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        scanf("%s",b);
        
        int l=-1000000000,r=1000000000,f=0;
        for(int i=0;i<n;i++){
            if(f<4) f++;
            else{
                if(b[i]=='1' && b[i-1]=='0'){
                    l=max(l,maxn(i)+1);
                    f=1;
                }
                else if(b[i]=='0' && b[i-1]=='1'){
                    r=min(r,minn(i)-1);
                    f=1;
                }
            }
        }
        printf("%d %d
    ",l,r);
    
        return 0;    
    }
    
    

    E. Cashback###

    给定一个n元素的数组a[],给定一个数c
    规定对于一个k元素的数组b[],其价值为b[]中所有元素的和 - 其中前(lfloor frac{k}{c} floor)小的元素的和
    将a数组分为若干个连续的子数组,求这些子数组的价值和的最小值
    (n leq 10^5)

    想法###

    贪心。
    价值和最小,即去掉的前(lfloor frac{k}{c} floor)小的元素的和最大
    我们可以发现,若有一个长度为2c的子数组,那么考虑这个子数组前2小的两个元素i与j
    若i与j同在这个子数组的前一半或后一半,那么将这个子数组分为两个长度为c的子子数组价值和会更小
    若它们一个在前一半,一个在后一半,那么可以直接将这个子数组分为前后两部分。
    故将a数组划分成的子数组长度为1或c
    dp+单调队列优化即可

    代码###

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const int N = 100005;
    
    int a[N];
    ll sum[N],f[N];
    int que[N],head,tail;
    int n,c;
    
    int main()
    {
        scanf("%d%d",&n,&c);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        
        for(int i=1;i<=n;i++){
            f[i]=f[i-1]+a[i];
            while(head<tail && que[head]<=i-c) head++;
            while(head<tail && a[que[tail-1]]>=a[i]) tail--;
            que[tail++]=i;
            if(i>=c) f[i]=min(f[i],f[i-c]+sum[i]-sum[i-c]-a[que[head]]);
        }
        printf("%lld
    ",f[n]);
        
        return 0;    
    }
    

    F. Machine Learning###

    给定一个数组a[],维护下面两种操作:
    1.求一段区间[l,r]的mex
    2.将a[p]的值改为x
    假设在某区间中每个数出现的次数为cnt[i]
    这个区间mex为cnt[]中不存在的最小的正整数

    想法###

    当时比赛的时候还不知道莫队算法
    后来才发现原来这就是个带修改莫队的模板题

    代码###

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<map>
    
    using namespace std;
    
    const int N = 200005;
    
    int read(){
        char ch=getchar();
        int x=0;
        if(!isdigit(ch)) ch=getchar();    
        while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
        return x;
    }
    
    int block;
    inline int bl(int x) { return (x-1)/block+1; }
    
    struct que{
        int l,r,tim,id;
        bool operator < (const que &b) const{
            return bl(l)<bl(b.l) || (bl(l)==bl(b.l) && bl(r)<bl(b.r)) || (bl(l)==bl(b.l) && bl(r)==bl(b.r) && tim<b.tim);     
        }
    }q[N];
    struct cg{
        int id,fr,to;       
    }d[N];
    
    int n,m,cnt1,cnt2;
    int a[N];
    map<int,int> num;
    int nn;
    
    int size[N],cnt[N],L,R;
    void add(int x){
        size[cnt[x]]--;
        size[++cnt[x]]++;
    }
    void del(int x){
        size[cnt[x]]--;
        size[--cnt[x]]++;     
    }
    void cg_add(cg w){
        if(L<=w.id && w.id<=R){
            del(w.fr);
            add(w.to);        
        }
        a[w.id]=w.to;
    }
    void cg_del(cg w){
        if(L<=w.id && w.id<=R){
            del(w.to);
            add(w.fr);
        }
        a[w.id]=w.fr;
    }
    
    int ans[N];
    inline int query(){
        for(int i=1;i<=n;i++)
            if(size[i]==0) return i;    
    }
    
    int main()
    {
        int opt,x,y;
        n=read(); m=read();
        for(int i=1;i<=n;i++) {
            a[i]=read();
            if(!num[a[i]]) num[a[i]]=++nn;
            a[i]=num[a[i]];
        }
        for(int i=1;i<=m;i++){
            opt=read(); x=read(); y=read();
            if(opt==1) q[++cnt1]=(que){x,y,cnt2,cnt1};
            else{
                if(!num[y]) num[y]=++nn;
                y=num[y];
                d[++cnt2]=(cg){x,a[x],y};
                a[x]=y;    
            }
        }
        
        block=pow(n,0.666);
        sort(q+1,q+1+cnt1);
        
        L=1; R=0; 
        int t=cnt2;
        for(int i=1;i<=cnt1;i++){
            while(R<q[i].r) add(a[++R]);
            while(L>q[i].l) add(a[--L]);
            while(R>q[i].r) del(a[R--]);
            while(L<q[i].l) del(a[L++]);
            while(t>q[i].tim) cg_del(d[t--]);
            while(t<q[i].tim) cg_add(d[++t]);
            
            ans[q[i].id]=query();
        }
        for(int i=1;i<=cnt1;i++) printf("%d
    ",ans[i]);
        
        return 0;    
    }
    

    终于……

    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    迅雷的工作原理 [揭密迅雷]
    揭密迅雷BT式下载本质 [揭密迅雷]
    2010年8月09日_周一_Toc control
    2010年8月08日_周日_MapCopyrightText control
    2010年8月08日_周日_Magnifier control
    2010年8月07日_周六_HoverExpandExtender control
    2010年8月11日_周三_ZoomLevel_control
    ArcGIS Server网站发布后地图显示空白的原因之一
    2010年8月10日_周二_TaskManagercontrol
    搭建一个简单的callBack函数
  • 原文地址:https://www.cnblogs.com/lindalee/p/8471362.html
Copyright © 2011-2022 走看看