Codeforces Gym 2015 ACM Amman Collegiate Programming Contest

Codeforces Gym 2015 ACM Amman Collegiate Programming Contest_第1张图片

比赛链接:

http://codeforces.com/gym/100712

题目链接:

http://codeforces.com/gym/100712/attachments/download/3454/acm-amman-collegiate-programming-contest-en.pdf


A. Who Is The Winner?

直接排序,复杂度O(nlogn)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
struct team
{
    string name;
    int s,p;
    bool operator < (const team &t)const
    {
        return s==t.s ? p<t.p : s>t.s;
    }
}t[105];
int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--)
    {
        int N;
        cin>>N;
        for(int i=1;i<=N;i++)
        {
            cin>>t[i].name>>t[i].s>>t[i].p;
        }
        sort(t+1,t+N+1);
        cout<<t[1].name<<endl;
    }
    return 0;
}


B. Rock-Paper-Scissors

分别预处理前k(k=0,1,2,...,n)次均出剪刀、石头或布的得分,O(n^2)枚举分界点即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
char s[1005];
int cnt[3][1005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",s);
        for(int i=1;i<=n;i++)
        {
            cnt[0][i]=cnt[0][i-1]+(s[i-1]=='S')-(s[i-1]=='P');
            cnt[1][i]=cnt[1][i-1]+(s[i-1]=='R')-(s[i-1]=='S');
            cnt[2][i]=cnt[2][i-1]+(s[i-1]=='P')-(s[i-1]=='R');
        }
        int ans=0;
        for(int i=0;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                //printf("%d %d\n",i,j);
                if(cnt[0][i]+cnt[1][j]-cnt[1][i]+cnt[2][n]-cnt[2][j]>0)ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


C. Street Lamps

将所有被路灯找到的格子标记为"*",于是每一段连续的k个"."对答案的贡献为ceil(k/3),复杂度O(n)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
char s[105];
bool isok[105];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",s);
        memset(isok,0,sizeof(isok));
        for(int i=0;i<n;i++)
        {
            if(i>0 && s[i-1]=='*')isok[i]=1;
            if(s[i]=='*')isok[i]=1;
            if(i<n-1 && s[i+1]=='*')isok[i]=1;
        }
        int loc=0,cnt=0,ans=0;
        while(loc<n)
        {
            while(loc<n && isok[loc])
            {
                ans+=(cnt+2)/3;
                cnt=0;
                loc++;
            }
            if(loc<n)
            {
                cnt++;
                loc++;
            }
        }
        ans+=(cnt+2)/3;
        printf("%d\n",ans);
    }
    return 0;
}


D. Alternating Strings

记dp[i]为将序列前i个字符按照要求划分所需要的最少段数,

若子串s[j..(i-1)]为“交替串”,则有dp[i]=max(dp[i],dp[j]+(i-j)),否则有dp[i]=max(dp[i],dp[j]+1),

暴力枚举j转移即可,复杂度O(n^2)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1005;
char s[MAXN];
int dp[MAXN];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        scanf("%s",s);
        memset(dp,0x3f3f3f3f,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            bool isok=0;
            for(int j=i-1;j>=max(0,i-k);j--)
            {
                if(j<i-1 && s[j]==s[j+1])isok=1;
                if(isok)dp[i]=min(dp[i],dp[j]+1);
                else dp[i]=min(dp[i],dp[j]+(i-j));
            }
        }
        printf("%d\n",dp[n]-1);
    }
    return 0;
}


E. Epic Professor

找出最大的数k,将所有数加上100-k之后统计>=50的数的个数即可,复杂度O(n)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int a[105];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        int mm=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            mm=max(mm,a[i]);
        }
        int add=100-mm;
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]+add>=50)ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}


F. Travelling Salesman

最小瓶颈生成树,跑一遍Kruskal即可,复杂度O(mlogm)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int MAXM=100005;
struct Edge
{
    int u,v,c;
    bool operator < (const Edge &t)const
    {
        return c<t.c;
    }
}e[MAXM];
int p[MAXN];
void Init(int n)
{
    for(int i=1;i<=n;i++)p[i]=i;
}
int Find(int x)
{
    return x==p[x] ? x : p[x]=Find(p[x]);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
        sort(e,e+m);
        int ans=0,dif=n;
        Init(n);
        for(int i=0;i<m;i++)
        {
            int t1=Find(e[i].u);
            int t2=Find(e[i].v);
            if(t1!=t2)
            {
                p[t1]=t2;
                dif--;
            }
            ans=max(ans,e[i].c);
            if(dif==1)break;
        }
        printf("%d\n",ans);
    }
    return 0;
}


G. Heavy Coins

暴力枚举所有硬币构成的集合,统计从该集合删去任意一枚硬币后总价值<=S的方案数,复杂度O(n*2^n)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int a[15];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,s;
        scanf("%d%d",&n,&s);
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        int ans=0;
        for(int mask=0;mask<(1<<n);mask++)
        {
            int tot=0,cnt=0;
            for(int i=0;i<n;i++)
                if(mask&(1<<i))
                {
                    cnt++;
                    tot+=a[i];
                }
            if(tot>=s)
            {
                bool isok=1;
                for(int i=0;i<n;i++)
                    if(mask&(1<<i))
                        if(tot-a[i]>=s)isok=0;
                if(isok)ans=max(ans,cnt);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


H. Bridges

对原图做一次双连通分量缩点,对缩点后得到树做两次BFS找出直径,该直径的长度即为最多可以减少的割边数,复杂度O(n+m)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN=100005;
const int MAXM=200005;
struct Edge
{
    int to,next;
    bool cut;
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];
int Index,top;
int block;
bool Instack[MAXN];
int bridge;
void addedge(int u,int v)
{
    edge[tot].to=v;
    edge[tot].next=head[u];
    edge[tot].cut=0;
    head[u]=tot++;
}
void Tarjan(int u,int pre)
{
    int v;
    Low[u]=DFN[u]=++Index;
    Stack[top++]=u;
    Instack[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        v=edge[i].to;
        if(v==pre)continue;
        if(!DFN[v])
        {
            Tarjan(v,u);
            if(Low[u]>Low[v])Low[u]=Low[v];
            if(Low[v]>DFN[u])
            {
                bridge++;
                edge[i].cut=1;
                edge[i^1].cut=1;
            }
        }
        else if(Instack[v] && Low[u]>DFN[v])
            Low[u]=DFN[v];
    }
    if(Low[u]==DFN[u])
    {
        block++;
        do
        {
            v=Stack[--top];
            Instack[v]=0;
            Belong[v]=block;
        }
        while(v!=u);
    }
}
vector<int>e[MAXN];
void init(int n)
{
    for(int i=1;i<=n;i++)e[i].clear();
    tot=0;
    memset(head,-1,sizeof(head));
}
int dis[MAXN];
void bfs(int st)
{
    queue<int>q;
    q.push(st);
    dis[st]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<e[u].size();i++)
        {
            if(dis[e[u][i]]>dis[u]+1)
            {
                q.push(e[u][i]);
                dis[e[u][i]]=dis[u]+1;
            }
        }
    }
}
void solve(int n)
{
    memset(DFN,0,sizeof(DFN));
    memset(Instack,0,sizeof(Instack));
    Index=top=block=bridge=0;
    Tarjan(1,0);
    for(int i=1;i<=n;i++)
        for(int j=head[i];j!=-1;j=edge[j].next)
            if(edge[j].cut)
            {
                e[Belong[edge[j].to]].push_back(Belong[i]);
                e[Belong[i]].push_back(Belong[edge[j].to]);
            }
    memset(dis,0x3f3f3f3f,sizeof(dis));
    bfs(1);
    int ans=0,loc=1;
    for(int i=1;i<=block;i++)
    {
        if(dis[i]>ans)
        {
            loc=i;
            ans=dis[i];
        }
    }
    memset(dis,0x3f3f3f3f,sizeof(dis));
    bfs(loc);
    ans=0;
    for(int i=1;i<=block;i++)ans=max(ans,dis[i]);
    printf("%d\n",bridge-ans);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        init(n);
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        solve(n);
    }
    return 0;
}


I. Bahosain and Digits

枚举k,枚举操作完成后所有位的结果,利用标记保证每一次检验的效率,复杂度O(10*n^2)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
char t[255],s[255];
int lazy[255];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",t);
        int n=strlen(t);
        int res=0;
        bool isok=0;
        for(int k=n;k>=1 && !isok;k--)
        {
            for(int d=0;d<=9 && !isok;d++)
            {
                strcpy(s,t);
                memset(lazy,0,sizeof(lazy));
                int cur=0;
                for(int i=0;i<=n-k;i++)
                {
                    s[i]=(s[i]-'0'+cur%10+10)%10+'0';
                    lazy[i]+=d+10-(s[i]-'0');
                    lazy[i+k-1]-=d+10-(s[i]-'0');
                    cur+=lazy[i];
                }
                for(int i=n-k+1;i<n;i++)
                {
                    s[i]=(s[i]-'0'+cur%10+10)%10+'0';
                    cur+=lazy[i];
                }
                //printf("%s\n",s);
                bool flag=1;
                for(int i=n-k+1;i<n;i++)
                    if(s[i]-'0'!=d)flag=0;
                if(flag)
                {
                    isok=1;
                    res=k;
                }
            }
        }
        printf("%d\n",res);
    }
    return 0;
}


J. Candy

直接维护两个优先队列,复杂度O(nlogn+mlogm)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
map<int,int>s,p;
map<int,int>::iterator itr_s,itr_p;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int in;
        s.clear();
        p.clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&in);
            s[in]++;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&in);
            p[in]++;
        }
        itr_s=s.begin();
        itr_p=p.begin();
        while(itr_s!=s.end() && itr_p!=p.end())
        {
            while(itr_p!=p.end() && itr_s->second > itr_p->second)p.erase(itr_p++);
            if(itr_p==p.end())break;
            s.erase(itr_s++);
            p.erase(itr_p++);
        }
        printf("%s\n",(s.empty() ? "YES" : "NO"));
    }
    return 0;
}


K. Runtime Error

记录每个数的出现次数,逐个枚举序列中的元素,注意x==0以及x==y的情形,复杂度O(n)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int a[100005];
int x[100005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        memset(x,0,sizeof(x));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            x[a[i]]++;
        }
        int mm=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            if(a[i]==0)continue;
            if(k%a[i]!=0)continue;
            if(x[k/a[i]]>1-(k/a[i]!=a[i]))mm=min(mm,a[i]);
        }
        if(mm==0x3f3f3f3f)printf("-1\n");
        else printf("%d %d\n",mm,k/mm);
    }
    return 0;
}


L. Alternating Strings II

此题是D题的加强版,

注意到“交替串”的长度具有二分性质,

如果用一个序列记录前i个字符中有多少对相邻的字符是不同的,

那么对于一个固定的i,可以二分出最小的j使得s[j..(i-1)]是“交替串”,

仍考虑D题的dp方程,

利用线段树分别维护dp[i]的最小值以及dp[i]-i的最小值,

得到分界点j之后,可以利用线段树上的区间查询加速转移,

复杂度O(nlogn)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int INF=0x3f3f3f3f;
char str[MAXN];
int pre[MAXN],dp[MAXN];
struct node
{
    int l,r,m,v[2];
}s[MAXN<<2];
void push_up(int n)
{
    for(int i=0;i<2;i++)
        s[n].v[i]=min(s[n<<1].v[i],s[n<<1|1].v[i]);
}
void build(int l,int r,int n)
{
    int m=(l+r)>>1;
    s[n].l=l;
    s[n].r=r;
    s[n].m=m;
    if(r-l==1)
    {
        s[n].v[0]=s[n].v[1]=INF;
        return;
    }
    build(l,m,n<<1);
    build(m,r,n<<1|1);
}
void update(int k,int p,int n)
{
    if(s[n].l==p && s[n].r==p+1)
    {
        s[n].v[0]=min(s[n].v[0],k);
        s[n].v[1]=min(s[n].v[1],k-(p-1));
        return;
    }
    if(p<s[n].m)update(k,p,n<<1);
    else update(k,p,n<<1|1);
    push_up(n);
}
int query(int l,int r,int n,int op)
{
    if(r<=l)return INF;
    if(s[n].l==l && s[n].r==r)return s[n].v[op];
    if(r<=s[n].m)return query(l,r,n<<1,op);
    if(l>=s[n].m)return query(l,r,n<<1|1,op);
    return min(query(l,s[n].m,n<<1,op),query(s[n].m,r,n<<1|1,op));
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        scanf("%s",str);
        pre[0]=0;
        for(int i=1;i<n;i++)
            pre[i]=pre[i-1]+(str[i]!=str[i-1]);
        memset(dp,INF,sizeof(dp));
        build(1,n+2,1);
        //printf("build done\n");
        dp[0]=0;
        update(dp[0],1,1);
        for(int i=1;i<=n;i++)
        {
            //printf("work on %d\n",i);
            int l=max(i-k,0),r=i-1;
            while(l<r)
            {
                int m=(l+r)>>1;
                if(pre[i-1]-pre[m]==i-m-1)r=m;
                else l=m+1;
            }
            //printf("left most %d\n",l);
            dp[i]=min(dp[i],query(max(i-k,0)+1,l+1,1,0)+1);
            dp[i]=min(dp[i],query(l+1,i+1,1,1)+i);
            //printf("%d %d %d\n",query(max(i-k,0)+1,l+2,1,0),query(l+2,i+1,1,1),dp[i]);
            update(dp[i],i+1,1);
        }
        printf("%d\n",dp[n]-1);
    }
    return 0;
}


你可能感兴趣的:(Codeforces Gym 2015 ACM Amman Collegiate Programming Contest)