• 2019牛客多校第六场


    A Garbage Classification

    题意

    给定一个字符串代表垃圾,26个字符每个字符代表某种组成成分,根据题意判断垃圾类别。

    分析

    温暖的签到题,注意别写成除法就行。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2050;
    int T;
    char s[N];
    char let[30];
    map<char,char> mp;
    int main(void){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            scanf("%s",s);
            scanf("%s",let);
            mp.clear();
            for(int i=0;i<26;i++){
                mp['a'+i]=let[i];
            }
            int d=0,w=0,h=0;
            int len=strlen(s);
            for(int i=0;i<len;i++){
                if(mp[s[i]]=='d'){
                    d++;
                }else if(mp[s[i]]=='w'){
                    w++;
                }else if(mp[s[i]]=='h'){
                    h++;
                }
            }
            printf("Case #%d: ",cas);
            if(4*h>=len){
                printf("Harmful
    ");
            }else if(10*h<=len){
                printf("Recyclable
    ");
            }else{
                if(d>=2*w){
                    printf("Dry
    ");
                }else{
                    printf("Wet
    ");
                }
            }
        }
        return 0;
    }
    

    B Shorten IPv6 Address

    题意

    给定一个128位的二进制数,先要转成32位十六进制数,每4位一组,中间用冒号分隔,然后如果一组都是0,那么4个0用1个0代替即可,然后可以选择一串且只能一串连续的0和冒号,替换成两个冒号,问可以得到的最小字典序是哪个。

    分析

    先构造出原字符串,然后对8个组,记录在字符串中的位置,然后枚举让每个组开头的连续0替换成双冒号后得到的字符串,直接暴力找出所有字符串,排序即可。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int T;
    char s[130];
    char hex(char a,char b,char c,char d){
        int t=(a-'0')*8+(b-'0')*4+(c-'0')*2+(d-'0');
        if(t<=9){
            return char(t+'0');
        }else{
            return char(t-10+'a');
        }
    }
    vector<string> vs;
    bool cmp(string a,string b){
        int al=a.size();
        int bl=b.size();
        if(al==bl){
            return a<b;
        }else{
            return al<bl;
        }
    }
    int idx[8];
    int main(void){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            scanf("%s",s);
            memset(idx,0,sizeof(idx));
            string he="";
            int cnt=0;
            for(int i=0;i<128;i+=16){
                string t="";
                bool ac=false;
                for(int j=i;j<i+16;j+=4){
                    char p=hex(s[j],s[j+1],s[j+2],s[j+3]);
                    if(!ac && p=='0'){
                        continue;
                    }
                    ac=true;
                    t+=p;
                }
                int q=he.size();
                if(t==""){
                    he+="0";
                }else{
                    he+=t;
                }
                idx[cnt++]=q;
                if(i==112){
                    continue;
                }
                he+=":";
            }
            vs.clear();
            vs.push_back(he);
            int hes=he.size();
            for(int i=0;i<8;i++){
                string tle="";
                int t=idx[i];
                if(i!=0){
                    t--;
                }
                for(int j=0;j<t;j++){
                    tle+=he[j];
                }
                int mle=0;
                while(t<hes && he[t]=='0' || he[t]==':'){
                    if(he[t]=='0'){
                        mle++;
                    }
                    t++;
                }
                if(mle>=2){
                    tle+="::";
                }else{
                    int j=idx[i];
                    if(i!=0){
                        j--;
                    }
                    for(;j<t;j++){
                        tle+=he[j];
                    }
                }
                for(int j=t;j<hes;j++){
                    tle+=he[j];
                }
                vs.push_back(tle);
            }
            sort(vs.begin(),vs.end(),cmp);
            printf("Case #%d: %s
    ",cas,vs[0].c_str());
        }
        return 0;
    }
    

    C Palindrome Mouse

    题意

    给定一个字符串,求多少对((a,b))满足(a)(b)都是原串的回文子串,且(a)也是(b)的子串。

    分析

    • 先用回文树构造出所有回文子串节点,对于每个节点来说,对答案的贡献由两部分组成,一部分是fail指针,比如aaa的fail指向aa,那么aa就是aaa的子串,且因为都是树上节点,即都是回文,另一部分是next指针,比如bb的next指向abba,那么bb就是abba的子串,且可以看出bb的fail指针b应该也是abba的子串。

    • 因此我们需要求出每个节点向上fail指针跳的次数和向下next指针跳的次数,乘法定理计算每个节点的贡献。

    • next次数的求法类似于dfs求子树大小,而在dfs的同时暴力往上求fail指针,为了避免重复计数,比如bb跳过的fail指针,bbbb不能再跳,需要暴力标记每个指针是哪个节点跳的,dfs完之后也是暴力清空标记。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+50;
    int vis[N],ndp[N],fdp[N];
    struct PT{
        int next[N][26],fail[N],cnt[N],num[N],len[N];
        int S[N],last,id[N],n,p;
        int newnode(int l){
            for(int i=0;i<26;i++){
                next[p][i]=0;
            }
            cnt[p]=num[p]=0;
            len[p]=l;
            return p++;
        }
        void init(){
            p=0;
            newnode(0);
            newnode(-1);
            last=0;
            n=0;
            S[n]=-1;
            fail[0]=1;
        }
        int getFail(int x){
            while(S[n-len[x]-1]!=S[n]){
                x=fail[x];
            }
            return x;
        }
        void add(int c){
            c-='a';
            S[++n]=c;
            int cur=getFail(last);
            if(!next[cur][c]){
                int now=newnode(len[cur]+2);
                fail[now]=next[getFail(fail[cur])][c];
                num[now]=num[fail[now]]+1;
                next[cur][c]=now;
            }
            last=next[cur][c];
            cnt[last]++;
            id[last]=n;
        }
        void count(){
            for(int i=p-1;i>=0;i--){
                cnt[fail[i]]+=cnt[i];
            }
        }
        int dfs(int u){
            ndp[u]=1;
            fdp[u]=0;
            //计算向上跳的fail指针次数,vis保证不重复(比如bb跳的fail指针,bbbb不能再跳),>1保证不走到奇偶根
            for(int t=u;!vis[t] && t>1;t=fail[t]){
                vis[t]=u;
                fdp[u]++;
            }
            for(int i=0;i<26;i++){
                if(next[u][i]){
                    ndp[u]+=dfs(next[u][i]);
                }
            }
            //清空标记
            for(int t=u;vis[t]==u && t>1;t=fail[t]){
                vis[t]=0;
            }
            return ndp[u];
        }
        ll solve(){
            //从两个根dfs
            dfs(0);
            dfs(1);
            ll ans=0;
            for(int i=2;i<p;i++){
                //除去根,每个节点的贡献(作为另一个回文子串的子串)为
                //比如对于样例abba,回文节点bb的next指针指向abba,fail指针指向b
                //因此ndp和fdp都为2,贡献为2*2-1=3
                //即(b,bb) (b,abba) (bb,bb) (bb,abba),减1就是要减掉本身
                ans+=1ll*ndp[i]*fdp[i]-1;
            }
            return ans;
        };
    }ac;
    int T;
    char s[N];
    int main(void){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            scanf("%s",s);
            ac.init();
            int len=strlen(s);
            for(int i=0;i<len;i++){
                ac.add(s[i]);
            }
            ac.count();
            memset(vis,0,sizeof(vis));
            printf("Case #%d: %lld
    ",cas,ac.solve());
        }
        return 0;
    }
    

    D Move

    题意

    (n)个物品,各自有体积(v_i),要用(k)个箱子装,装的策略是贪心从大往小,能装就装,问每个箱子最小的容量。

    分析

    • 由于贪心的策略,使得箱子容量并不满足单调性,有可能小一点的容量可以,但是如果容量大一点,可能会先取装多个大的,导致小的太多刚好装不下。

    • 最简单的做法是直接从容量的下界(max(v_n,(sum-1)/k+1))开始暴力枚举,然后用multiset判断。

    • 上界的证明:

      • 假设某个容量(V)装不下,那么每个箱子剩余容量都小于(maxV)
      • 那么(k*(V-maxV+1)<=sum)
      • 得到(V<=sum/k+maxV-1)
    • 因为(v_i)的范围比较小,所以可以感性地理解一下,然后发现二分再暴力一段小区间也是可以的。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1005;
    int T,n,k,a[N];
    multiset<int> mt;
    bool check(int x){
        mt.clear();
        for(int i=1;i<=n;i++){
            mt.insert(a[i]);
        }
        for(int i=0;i<k;i++){
            int u=0;
            while(u<x && !mt.empty()){
                auto p=mt.upper_bound(x-u);
                if(p==mt.begin()){
                    break;
                }
                p--;
                u+=*p;
                mt.erase(p);
            }
        }
        return mt.empty();
    }
    int main(void){
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            scanf("%d%d",&n,&k);
            int sum=0;
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                sum+=a[i];
            }
            sort(a+1,a+1+n);
            //从下界暴力枚举
            int ans;
            for(int v=max(a[n],(sum-1)/k+1);;v++){
                if(check(v)){
                    ans=v;
                    break;
                }
            }
            printf("Case #%d: %d
    ",cas,ans);
        }
        return 0;
    }
    

    G Is Today Friday?

    题意

    给n个日期字符串,A到J代表0到9的一个排列,求一个字典序最小的排列使得这n个日期都是星期五。

    分析

    • 因为有星期五这个限制,所以对前几个日期,先暴力全排列,找出满足条件的全排列,再从这些全排列中对剩下的日期进行判断。

    • 根据日期计算周几可以用蔡勒公式或者基姆拉尔森公式

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+50;
    int T,n;
    struct node{
        char str[12];
        bool operator<(const node& rhs)const{
            return string(str)<string(rhs.str);
        }
        bool operator==(const node& rhs)const{
            return string(str)==string(rhs.str);
        }
    }s[N];
    vector<int> mp;
    vector<vector<int> > ts;
    int ms[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    bool leap(int y){
        return ((y%4==0 && y%100!=0) || y%400==0);
    }
    //判断日期合法性
    bool check(int y,int m,int d){
        if(m==0 || m>12 || d==0){
            return false;
        }
        int t=ms[m];
        if(leap(y) && m==2){
            t++;
        }
        return d<=t;
    }
    //基姆拉尔森公式
    bool cl(int y,int m,int d){
        if(!check(y,m,d)){
            return false;
        }
        //题面...
        if(y<1600){
            return false;
        }
        //1 2月要转成上一年13 14月
        if(m<=2){
            m+=12;
            y--;
        }
        int w=((d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7+7)%7;
        return w==5;
    }
    int main(void){
        // freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            scanf("%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%s",s[i].str);
            }
            printf("Case #%d: ",cas);
            sort(s+1,s+1+n);
            int nn=unique(s+1,s+1+n)-s-1;
            //先根据前几个日期筛选出满足条件的全排列(不多),再代入剩下的日期判断
            //ts记得清空
            ts.clear();
            mp.clear();
            for(int i=0;i<10;i++){
                mp.push_back(i);
            }
            do{
                bool ac=true;
                //前4个
                for(int i=1;i<=min(nn,4);i++){
                    int y=mp[s[i].str[0]-'A']*1000+mp[s[i].str[1]-'A']*100+mp[s[i].str[2]-'A']*10+mp[s[i].str[3]-'A'];
                    int m=mp[s[i].str[5]-'A']*10+mp[s[i].str[6]-'A'];
                    int d=mp[s[i].str[8]-'A']*10+mp[s[i].str[9]-'A'];
                    if(!cl(y,m,d)){
                        ac=false;
                        break;
                    }
                }
                if(ac){
                    ts.push_back(mp);
                }
            }while(next_permutation(mp.begin(),mp.end()));
            //判断剩下n-4个
            int sz=ts.size();
    //        for(int i=0;i<sz;i++){
    //            for(int j=0;j<10;j++){
    //                printf("%d",ts[i][j]);
    //            }
    //            printf("
    ");
    //        }
            //考虑nn<=4的情况
            if(nn<=4){
                if(sz){
                    for(int i=0;i<10;i++){
                        printf("%d",ts[0][i]);
                    }
                    printf("
    ");
                }else{
                    printf("Impossible
    ");
                }
                continue;
            }
            bool ok=false;
            for(int i=0;i<sz;i++){
                bool ac=true;
                for(int j=5;j<=nn;j++){
                    int y=ts[i][s[j].str[0]-'A']*1000+ts[i][s[j].str[1]-'A']*100+ts[i][s[j].str[2]-'A']*10+ts[i][s[j].str[3]-'A'];
                    int m=ts[i][s[j].str[5]-'A']*10+ts[i][s[j].str[6]-'A'];
                    int d=ts[i][s[j].str[8]-'A']*10+ts[i][s[j].str[9]-'A'];
                    if(!cl(y,m,d)){
                        ac=false;
                        break;
                    }
                }
                if(ac){
                    for(int j=0;j<10;j++){
                        printf("%d",ts[i][j]);
                    }
                    printf("
    ");
                    ok=true;
                    break;
                }
            }
            if(!ok){
                printf("Impossible
    ");
            }
        }
        return 0;
    }
    

    J Upgrading Technology

    题意

    (n)个技能,每个技能有(m)个等级,第(i)技能从(j-1)级升到(j)级需要花费(c_{ij})(n)个技能都升到(i)级可以获得收益(d_i),问最大收益。

    分析

    题意中暗示可以(O(nm))的复杂度,因此考虑枚举所以技能同时升到(i)级,此时的收益可以前缀和求出,再枚举最低等级也就是刚好(i)级的技能,那么这个技能的花费也能前缀和求出,而其他技能就可以在(i)级后面随便升了,我们只需要预处理出每个技能在某个级别之后升级的最小花费以及n个技能的最小花费和。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1005;
    int T,n,m;
    ll c[N][N],d[N];
    ll dp[N],cp[N][N];
    ll mn[N][N],smn[N];
    int main(void){
        freopen("in.txt","r",stdin);
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            ll ans=0;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                cp[i][0]=0;
                for(int j=1;j<=m;j++){
                    scanf("%lld",&c[i][j]);
                    cp[i][j]=cp[i][j-1]+c[i][j];
                }
            }
            //mn[i][j]表示第i个技能从第j个等级开始的最小前缀和
            memset(smn,0,sizeof(smn));
            for(int i=1;i<=n;i++){
                mn[i][m]=cp[i][m];
                ll _mn=mn[i][m];
                smn[m]+=cp[i][m];
                for(int j=m-1;j>=0;j--){
                    if(cp[i][j]<_mn){
                        _mn=cp[i][j];
                    }
                    mn[i][j]=_mn;
                    smn[j]+=mn[i][j];
                }
            }
            dp[0]=0;
            for(int i=1;i<=m;i++){
                scanf("%lld",&d[i]);
                dp[i]=dp[i-1]+d[i];
            }
            for(int i=0;i<=m;i++){
                //枚举相同等级,可以都不升级
                for(int j=1;j<=n;j++){
                    ll tmp=dp[i];
                    //枚举最低等级的技能
                    tmp-=cp[j][i];
                    //剩下的技能找大于等于i级别的最小前缀和之和
                    tmp-=smn[i];
                    //最低等级的技能被重复减了
                    tmp+=mn[j][i];
                    ans=max(ans,tmp);
                }
            }
            printf("Case #%d: %lld
    ",cas,ans);
        }
        return 0;
    }
    
  • 相关阅读:
    Syntax error, insert "]" to complete MemberExpression XXX.js (Java Web Project 导入Jquery的文件后报错)
    Unicode 转换成 Ascii (把Unicode 中文字符串输入到文本中)
    Static控件响应鼠标事件
    在Window 7 64位操作系统上安装Oracle 10g 及 配置PLSQL Developer 8.0.4图解
    LPSTR LPCSTR LPWSTR LPCWSTR区别
    C++ Builder 全部API函数列表
    CPropertySheet标签页 实现各个CPropertyPage页面之间的切换
    C#中如何从字符串中提取数字
    如何用SQL统计某个字符在一个字符串中出现的次数
    MVC中--异常详细信息: System.ArgumentNullException: 值不能为 null。 参数名: value
  • 原文地址:https://www.cnblogs.com/zxcoder/p/11301002.html
Copyright © 2020-2023  润新知