zoukankan      html  css  js  c++  java
  • 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    传送门

    A.Altruistic Amphibians

        一个挺有意思的题目。首先考虑这样的问题:倘若要让尽可能多的青蛙能够逃跑,则显然罗汉最好叠得尽可能的高(这才能使得那些不能一次性跳出的青蛙能够逃离)。

        而显然,对于那些体重最大的青蛙,他们显然不能叠在其他青蛙上,因此我们首先对青蛙的重量从大到小进行排序,其次我们考虑第i个青蛙的重量对于其他重量小的重量的青蛙的状态的转移。

        我们设dp[i]为重量为i的青蛙最高能够跳的高度,而对于第i个重量为w[i]的青蛙,不难想到最多一定会有个w[i]个重量小于w[i]的青蛙能够跳到它的上面,故可得有状态转移方程(dp[j-w[i]]=max(dp[j-w[i]],dp[j]+h[i])(w[i]<=j<=2*w[i])

    #include <bits/stdc++.h>
    #define maxn 100000005
    using namespace std;
    int dp[maxn];
    struct Node{
        int l,w,h;
        bool operator <(const Node &b)const{
            return w>b.w;
        }
    }q[100005];
    int main()
    {
        int n,d;
        scanf("%d%d",&n,&d);
        for(int i=0;i<n;i++) scanf("%d%d%d",&q[i].l,&q[i].w,&q[i].h);
        sort(q,q+n);
        int res=0;
        for(int i=0;i<n;i++){
            if(dp[q[i].w]+q[i].l>d) res++;
            for(int j=q[i].w;j<min(2*q[i].w,(int)1e8+2);j++){
                dp[j-q[i].w]=max(dp[j-q[i].w],dp[j]+q[i].h);
            }
        }
        printf("%d
    ",res);
    }
    View Code

     B.Baby Bites

        温暖的签到题

    #include<bits/stdc++.h>
    using namespace std;
    string str;
    int change(string s){
        int len=s.size();
        int ret=0;
        for(int i=0;i<len;i++){
            ret=ret*10+str[i]-'0';
        }
        return ret;
    }
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n;
        cin>>n;
        int flag=0;
        for(int i=1;i<=n;i++){
            cin>>str;
            if(str[0]>='0'&&str[0]<='9'){
                int num=change(str);
                if(num!=i) flag=1;
            }
        }
        if(flag) cout<<"something is fishy"<<endl;
        else cout<<"makes sense";;
        return 0;
    
    }
    View Code

    C.Code Cleanups

        题意相当奇怪的签到题。实际的题意为:给你若干个操作,每个操作的序号id都是递增的。在第id天一定会进行该次操作。每次进行一次操作,肮脏值就会+1(最开始肮脏值为0),肮脏值每天都累计,直到在某一天,累计的肮脏值大于20,则将所有的肮脏值清零,现在问你最小的清零的次数。

        这个题明白题意后就是一个很简单的题目了

    #include<bits/stdc++.h>
    using namespace std;
    int mp[400];
    int main(){
        int n;
        scanf("%d",&n);
        int tmp;
        for(int i=1;i<=n;i++){
            scanf("%d",&tmp);
            mp[tmp]=1;
        }
        tmp=0;
        int sum=0;
        int ans=0;
        for(int i=1;i<=365;i++){
            if(mp[i]) tmp+=1;
            sum+=tmp;
            if(sum>=20){
                ans++;
                tmp=sum=0;
            }
        }
        if(sum) ans++;
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    D.Delivery Delays

     因为要找最大值最小,所以考虑二分,但是如何很快地check。送货员在跑的时候一定会沿最短路跑,所以先做一下多源的最短路。题目说送货员会按照下单顺序送货,所以送货员送货的过程将会是分成几段出去回来的过程。所以check的时候就看一下是否有一个分段的方案使得答案可行。dp[i]表示前i个送完回到1号点的最小时间,有了dp[i]就可以更新假如后面一个长度为len的区间出去一次就连续送完的答案,需要判断这一个区间如果出去一次就送完是否可行,这就需要找到送货远最早以及最晚什么时间出发,判断要更新的时间是否落在这个时间段内,如果满足方可更新。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 typedef pair<ll,int>pll;
     5 const int maxn=1005;
     6 const int maxm=5005;
     7 const long long inf=0x3f3f3f3f3f3f3f3f;
     8 long long dp[maxn];
     9 ll s[maxn],t[maxn];
    10 int p[maxn];
    11 int n,m,k;
    12 struct edge{
    13     int to,nxt;
    14     ll d;
    15 }e[maxm<<1];
    16 int head[maxn];
    17 long long dis[maxn][maxn];
    18 int ecnt;
    19 void add_edge(int u,int v,ll d){
    20     e[ecnt].to=v;
    21     e[ecnt].d=d;
    22     e[ecnt].nxt=head[u];
    23     head[u]=ecnt++;
    24 }
    25 void ins(int u,int v,ll d){
    26     add_edge(u,v,d);
    27     add_edge(v,u,d);
    28 }
    29 void dij(int id,int u){
    30     priority_queue<pll,vector<pll>,greater<pll> >q;
    31     for(int i=1;i<=n;i++) dis[id][i]=inf;
    32     dis[id][u]=0;
    33     q.push(make_pair(dis[id][u],u));
    34     while(!q.empty()){
    35         long long tmp=q.top().first;
    36         u=q.top().second;
    37         q.pop();
    38         if(dis[id][u]<tmp) continue;
    39         for(int i=head[u];~i;i=e[i].nxt){
    40             int v=e[i].to;
    41             if(dis[id][v]>dis[id][u]+e[i].d){
    42                 dis[id][v]=dis[id][u]+e[i].d;
    43                 q.push(make_pair(dis[id][v],v));
    44             }
    45         }
    46     }
    47 }
    48 bool check(long long x){
    49     memset(dp,inf,sizeof(dp[0])*(n+2));
    50     dp[0]=0;
    51     for(int i=0;i<k;i++){
    52         long long st=dp[i];
    53         long long d=0;
    54         long long lt=inf;
    55         for(int len=1;i+len<=k;len++){
    56             int to=i+len;
    57             if(to==i+1) d+=dis[1][p[to]];
    58             else d+=dis[p[to-1]][p[to]];
    59             st=max(st,t[to]);lt=min(x-d+s[to],lt);
    60             long long tt=st+d-s[to];
    61             if(tt<=x&&st<=lt) dp[to]=min(dp[to],st+d+dis[p[to]][1]);
    62             else break;
    63         }
    64     }
    65     if(dp[k]<inf) return 1;
    66     return 0;
    67 }
    68 int main(){
    69     ios::sync_with_stdio(false);
    70     cin.tie(0);
    71     cin>>n>>m;
    72     int u,v;
    73     long long w;
    74     memset(head,-1,sizeof(head[0])*(n+2));
    75     for(int i=1;i<=m;i++){
    76         cin>>u>>v>>w;
    77         ins(u,v,w);
    78     }
    79     for(int i=1;i<=n;i++){dij(i,i);}
    80     cin>>k;
    81     for(int i=1;i<=k;i++)
    82     cin>>s[i]>>p[i]>>t[i];
    83     long long l=0,r=inf;
    84     long long ans=l;
    85     while(l<=r){
    86         long long mid=(l+r)>>1;
    87         if(check(mid)){ans=mid;r=mid-1;}
    88         else l=mid+1;
    89     }
    90     cout<<ans<<endl;
    91     return 0;
    92 }
    View Code

    E.Delivery Delays

        首先我们可以发现,因为每个士兵的血量最大为6,且敌我双方的士兵数均为5,因此我们考虑可以用搜索的方法去解决。

        因为士兵的总血量的状态比较少,因此我们可以考虑用一个12位的long long的每一位去存储每一种血量的个数。此时,这每一个12位的long long整型就唯一代表了一种状态。而又因为在搜索的过程中,每一种曾经访问过的状态所对应的概率必定是唯一的,因此我们只需要用记忆化的形式对曾经出现过的结果记进行记录,以达到剪枝的作用。

        因为我们要记录的是敌军死亡的概率,因此,我们可以优先将敌军的6种血量置于12位long long的高位,这样,当我们访问到的状态值<1000000,则代表已经敌军已经已经死亡,即可直接跳出递归(又一个剪枝)。

        最后只需要将相应的概率相乘并相加即为答案。

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ll;
    int mp[2][10];
    unordered_map<ll,double>dp;//充当记忆化数组
    ll GetSta(){//获取状态
        ll res=0;
        for(int i=1;i<=6;i++) res*=10,res+=mp[1][i];
        for(int i=1;i<=6;i++) res*=10,res+=mp[0][i];
        return res;
    }
    double dfs(ll sta,int limit){
        if(dp.count(sta)) return dp[sta];//如果该状态曾经访问过,则直接调用结果
        if(sta<1000000) return 1;//如果该状态的值<1000000,则证明敌人已死,返回1
        if(limit==0) return 0;
        int cnt=0;
        for(int i=0;i<2;i++)//获取总人数
            for(int j=1;j<=6;j++) cnt+=mp[i][j];
        double res=0;
        for(int i=0;i<2;i++){
            for(int j=1;j<=6;j++){
                if(!mp[i][j]) continue;
                mp[i][j]--;
                mp[i][j-1]++;
                ll newsta=GetSta();
                double tmp=dfs(newsta,limit-1);//dfs求解下一层的答案
                dp[newsta]=tmp;
     
                mp[i][j]++;//回溯
                mp[i][j-1]--;
     
                res+=1.0*mp[i][j]/cnt*tmp;//统计概率
            }
        }
        return res;
    }
    int main()
    {
        int n,m,d,num;
        scanf("%d%d%d",&n,&m,&d);
        for(int i=0;i<n;i++){
            scanf("%d",&num);
            mp[0][num]++;
        }
        for(int i=0;i<m;i++){
            scanf("%d",&num);
            mp[1][num]++;
        }
        double res=dfs(GetSta(),d);
        printf("%.8f
    ",res);
    }
    View Code

    F.Firing the Phaser


    G.Game Scheduling


    H.House Lawn

        为了保证对于任意的T而言,T周一定能够完成T项任务,因此我们只需要对每一件商品枚举周期T(0<=T<=间隔+工作时间),判断是否能在某个整周期完成至少一次工作即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=105;
    double eps=1e-8;
    int sgn(double x){
        if(fabs(x)<eps) return 0;
        if(x<eps) return -1;
        else return 1;
    }
    int n,m;
    struct data{
        string s;
        int p,c,t,r;
        int id;
        bool operator < (const data& x)const{
            if(p==x.p) return id>x.id;
            return p>x.p;
        }
    }d[maxn];
    int main(){
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        scanf("%d%d",&n,&m);
        priority_queue<data>q;
        for(int i=1;i<=m;i++){
            getchar();
            char ch=getchar();
            while(ch!=','){d[i].s+=ch;ch=getchar();}
            scanf("%d,%d,%d,%d",&d[i].p,&d[i].c,&d[i].t,&d[i].r);
            d[i].id=i;
        }
        for(int i=1;i<=m;i++){
            bool flag=0;
            for(int T=1;T<=d[i].t+d[i].r;T++){
                if(flag) break;
                long long sum1=10080*T;
                long long sum=0;
                long long tmp=sum1/(d[i].r+d[i].t);
                tmp*=d[i].t;
                long long tmp1=sum1%(d[i].r+d[i].t);
                tmp1=min(tmp1,1LL*d[i].t);
                sum+=(tmp+tmp1)*d[i].c;
                if(sum<1LL*n*T) flag=1;
            }
            if(!flag) q.push(d[i]);
        }
        if(q.empty()){puts("no such mower");return 0;}
        data u=q.top();
        q.pop();
        cout<<u.s<<endl;
        while(!q.empty()&&u.p==q.top().p){
            cout<<q.top().s<<endl;
            q.pop();
        }
        return 0;
    }
    View Code

    I.Intergalactic Bidding

         题目说了从大到小排完序后所有人的赌注都大于后面那个人的两倍,所以题解就是高精度排序,然后高精度减法,减到最后判断最后那个数是否为0就行。

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn=1500;
    struct bigint{
        int len;
        int  a[maxn];
        bigint (){
            len=0;
            memset(a,0,sizeof a);
        }
        bool operator <(const bigint &b)const {
            if (len<b.len) return 1;
            if (len>b.len) return 0;
            for (int i=len-1;i>=0;i--)
                if (a[i]>b.a[i]) return 0;
                else if (a[i]<b.a[i]) return 1;
            return 1;
        }
        bigint operator -(const bigint &b)const {
            bigint c;
            c.len=len;
            for (int i=0;i<len;i++) c.a[i]=a[i];
            int l=len;
            for (int i=0;i<l;i++) {
                if (c.a[i]<b.a[i]) {
                    c.a[i+1]--;
                    c.a[i]+=10;
                }
                c.a[i]-=b.a[i];
            }
            while (c.len!=0&&c.a[c.len-1]==0) c.len--;
            return c;
        }
    }m;
    bigint change(char *s){
        bigint c;
        int len=strlen(s);
        for (int i=len-1;i>=0;i--)
            c.a[c.len++]=s[i]-'0';
        return c;
    }
    struct S{
        int id;
        char s[maxn];
        bigint a;
        bool operator <(const S &b)const {
            return b.a<a;
        }
    }f[maxn];
    char s[maxn];
    int ans[maxn],cnt;
    int main(){
        int n;
        cin>>n>>s;
        m=change(s);
        for (int i=1;i<=n;i++) {
            cin>>f[i].s>>s;
            f[i].a=change(s);
        }
        sort(f+1,f+n+1);
        for (int i=1;i<=n;i++){
            if (f[i].a<m){
                m=m-f[i].a;
                ans[cnt++]=i;
            }
        }
        if (m.len!=0) cnt=0;
        cout<<cnt<<endl;
        for (int i=0;i<cnt;i++) cout<<f[ans[i]].s<<endl;
        return 0;
    }
    /*
    6 6300000000000000000000
    dasf 1
    Vader 299999999999999999999
    Voldemort 700000000000000000000
    BorgQueen 2000000000000000000000
    Terminator 4000000000000000000000
    Megatron 10100000000000000000000
     */
    View Code

    J.Jumbled String

      构造题,我们可以先根据a和d,算出我们需要的0和1的数量,如果算不出来,impossible;接着我们可以判断一下0和1的个数相乘是否为b+c,如果不等,impossible;如果相等,最后就是根据b和c调整0和1的位置输出即可。

      要注意特判一下几种特殊情况,a、b、c、d都为0时,不是impossible,要输出一个0或1;

      当a=b=c=0,d!=0或b=c=d=0,a!=0时,要输出对应全0或全1的序列,不行则impossible。

    #include<bits/stdc++.h>
    using namespace std;
    bool check(long long x,long long ans){
        if(x*(x-1)/2<=ans) return 1;
        return 0;
    }
    int main(){
      //  freopen("in.txt","r",stdin);
     //   freopen("out.txt","w",stdout);
        long long  a,b,c,d;
        scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        if (a==0&&b==0&&c==0&&d==0) {
            printf("0
    ");
            return 0;
        }
        long long l=1,r=1e9;
        long long ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid,a)) {l=mid+1;ans=mid;}
            else r=mid-1;
        }
        long long ans1=0;
        l=1,r=1e9;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid,d)) {l=mid+1;ans1=mid;}
            else r=mid-1;
        }
      //  cout<<ans<<' '<<ans1<<endl;
        if (a==0&&b==0&&c==0&&d!=0){
            if (ans1*(ans1-1)/2==d) {
                for (int i=1;i<=ans1;i++) printf("1");
                printf("
    ");
            }else printf("impossible
    ");
            return 0;
        }
          if (a!=0&&b==0&&c==0&&d==0){
            if (ans*(ans-1)/2==a) {
                for (int i=1;i<=ans;i++) printf("0");
                printf("
    ");
            }else printf("impossible
    ");
            return 0;
        }
    
    
        if (ans1*ans!=b+c||ans*(ans-1)/2!=a||ans1*(ans1-1)/2!=d) printf("impossible
    ");
        else{
            long long tmp=b%ans,tmp2=b/ans;
            if (tmp==0) {
                tmp=ans;
                for (int i=1;i<=ans1-tmp2;i++) printf("1");
                for(int i=1;i<=ans;i++) printf("0");
                for (int i=ans1-tmp2+1;i<=ans1;i++) printf("1");
                printf("
    ");
            }else {
                for (int i=1;i<ans1-tmp2;i++) printf("1");
                for(int i=1;i<=tmp;i++) printf("0");
                printf("1");
                for (int i=tmp+1;i<=ans;i++) printf("0");
                for (int i=ans1-tmp2+1;i<=ans1;i++) printf("1");
                printf("
    ");
            }
        }
        return 0;
    }
    View Code

    K.King's Colors

      给你一棵树,问用恰好k种颜色将其涂成两个相邻点颜色不同的方法有多少种。题目告诉你是一棵树了,你涂根节点有k种方法,在接下去的n-1个点的涂法就都是(k-1)种,所以答案为k*(k-1)^(n-1)。仔细想想就会发现怎么涂和树的形状没有关系,所以不用理会。接着,题目说的是要恰好k种,明显我们上面的式子不是恰好k种,所以我们要容斥一下,容斥系数就是一个组合数,把恰好(k-1)种的情况减去,把恰好(k-2)种的情况加上…… 最后就是答案了。

    #include <iostream>
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    const int maxn=2507;
    ll inv[maxn],f[maxn],finv[maxn];
    ll powm(ll x,ll n){
        ll res=1;
        while (n){
            if (n&1) res=res*x%mod;
            n>>=1;
            x=x*x%mod;
        }
        return res;
    }
    void init(){
        inv[1]=1;
        for (int i=2;i<maxn;i++)
            inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        f[0]=finv[0]=1;
        for (int i=1;i<maxn;i++){
            f[i]=f[i-1]*i%mod;
            finv[i]=finv[i-1]*inv[i]%mod;
        }
    }
    ll C(ll n,ll m){
        if (n<0||m<0||m>n) return 0;
        return f[n]*finv[m]%mod*finv[n-m]%mod;
    }
    int main(){
        init();
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n,k;
        cin>>n>>k;
        ll ans=0;
        for (int i=0,f=1;i<k;i++,f=-f)
            ans=(ans+f*(k-i)%mod*powm(k-i-1,n-1)%mod*C(k,i)%mod+mod)%mod;
        cout<<ans<<endl;
        return 0;
    }
    View Code
  • 相关阅读:
    数据结构小练习
    【BZOJ 3652】大新闻 数位dp+期望概率dp
    【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化
    【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黄昏下的礼物 dp转有限状态自动机+矩阵乘法优化
    【BZOJ 4455】 [Zjoi2016]小星星 容斥计数
    凸包小结
    Matrix-Tree定理题表
    PLAN OF HEOI(unfinished)
    (ex)BSGS题表
    exBSGS板子
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/9940080.html
Copyright © 2011-2022 走看看