2017 summer individual contest(6)

A.Restaurant Tables(模拟)

题意:
a张单人桌,b张双人桌,有n组人来餐厅吃饭,每组只有一个或两个人。如果是一个人来,会给他安排单人桌,如果没有单人桌,则安排双人桌,如果仍然没有双人桌,则安排在已经有一位客人的双人桌,若仍然没有,只能拒绝提供服务。如果该组有两个人,则安排双人桌,若没有双人桌,则拒绝服务。按来的顺序给出每组人数(1 or 2),问需要拒绝为多少人服务?

题解:
注意决策顺序。一个人:单人桌→双人桌→只有一个人的双人桌→拒绝服务;两个人:双人桌→拒绝服务

题目链接:http://codeforces.com/problemset/problem/828/A

代码:

#include<stdio.h>

int n,a[4],ans,num;
int main()
{
    while (scanf("%d %d %d",&n,&a[1],&a[2])!=EOF)
    {
        ans = 0;
        a[3] = 0;
        for (int i=0;i<n;i++)
        {
            scanf("%d",&num);
            if (num == 1)
            {
                if (a[1]) a[1]--;
                else
                    if (a[2]) a[2]--,a[3]++;
                    else
                        if (a[3]) a[3]--;
                    else
                        ans++;
            }
            else
            {
                if (a[2]) a[2]--;
                else
                    ans+=2;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

B.Black Square(模拟)

题意:
给出n*m矩阵包含黑色块和白色块,可以选择将白色块涂成黑色块,问最少需要涂多少个白色块,才能使所有黑色块构成一个正方形,若不能,输出-1。

题解:
找出最边缘的黑色块,将其进行左右扩展和上下扩展,成为正方形

题目链接:http://codeforces.com/problemset/problem/828/B

代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;

char a[105][105];
int n,m,x,xx,y,yy,ans,len,k;

int main()
{
    while (scanf("%d %d",&n,&m)!=EOF)
    {
        x = n, yy = m, y = -1, xx = -1;
        ans = 0;
        for (int i=0;i<n;i++)
        {
            scanf("%s",a[i]);
            for (int j=0;j<m;j++)
                if (a[i][j] == 'B')
            {
                x = min(i,x);
                y = max(j,y);
                xx = max(i,xx);
                yy = min(j,yy);
            }
        }
        //printf("%d %d %d %d\n",x,xx,y,yy);
        if (xx == -1) ans=1;
        else
        {
            len = max(y-yy+1,xx-x+1);
            k = len - (xx-x+1);
            while (x>0 && k>0) x--,k--;
            while (xx<n-1 && k>0) xx++,k--;
            if (k > 0)
            {
                printf("-1\n");
                continue;
            }
            k = len - (y-yy+1);
            while (yy>0 && k>0) yy--,k--;
            while (y<m-1 && k>0) y++,k--;
            if (k > 0)
            {
                printf("-1\n");
                continue;
            }
            for (int i=x;i<=xx;i++)
                for (int j=yy;j<=y;j++)
                if (a[i][j] == 'W') ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

C. String Reconstruction(模拟,排序)

题意:
每行开头输入一个字符串,并给出字符串出现的位置,问字典序最小的总串

题解:
对字符串的位置进行排序,遍历一遍,输出字符串,未知位置填’a’

题目链接:http://codeforces.com/problemset/problem/828/C

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define For(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;

const int maxn = 1e6+10;
struct mark
{
    int k,w;
}str[maxn*10];
char ans[maxn*10];
string c[maxn];
int Case,n,a,num;

bool cmp(mark x,mark y)
{
    if (x.w < y.w) return 1;
    else
        if (x.w == y.w && c[x.k].size() > c[y.k].size())
            return 1;
    return 0;
}

int main()
{
    scanf("%d",&Case);
    For (t,0,Case)
    {
        cin >> c[t];
        scanf("%d",&n);
        For (i,0,n)
        {
            scanf("%d",&a);
            str[num].k = t;
            str[num].w = a;
            num++;
        }
    }
    sort(str,str+num,cmp);
    int len = 0;
    For (i,0,num)
    {
        int ki = str[i].k;
        if (len < str[i].w)
        {
            len = str[i].w - 1;
            For (j,0,c[ki].size())
                ans[++len] = c[ki][j];
        }
        else
        {
            int start = len - str[i].w + 1;
            For (j,start,c[ki].size())
                ans[++len] = c[ki][j];
        }
    }
    For (i,1,len+1)
        if (ans[i] >= 'a' && ans[i]<='z')
            printf("%c",ans[i]);
        else
            printf("a");
    return 0;
}

D.High Load(贪心,构造图)

题意:
n个结点,k个结点的度为1,其余结点的度大于1,问如何连接使得距离最远的两个结点离得最近。

题解:
以一个结点为中心向k个方向尽量扩展,每个方向向外扩展的点数为(n-1)/k或(n-1)/k+1;如果所有向外扩展的点数为(n-1)/k,则距离为(n-1)/k,如果存在一个方向向外扩展的点数为(n-1)/k+1,则距离为(n-1)/k+1,如果存在大于两个方向向外扩展的点数为(n-1)/k+1,则距离为(n-1)/k+2。

题目链接:http://codeforces.com/problemset/problem/828/D

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>

using namespace std;

const int maxn = 2*1e5+100;
vector <int> e[maxn];
int s,n,rem,k,node;

int main()
{
    while (scanf("%d %d",&n,&k)!=EOF)
    {
        s = (n-1) / k;
        rem = (n-1) % k;
        if (rem == 0) printf("%d\n",2*s);
        else
            if (rem == 1) printf("%d\n",2*s+1);
            else
                printf("%d\n",2*s+2);
        for (int i=1;i<=n;i++) e[i].clear();
        node = 1;
        while (node < n)
        {
            node++,e[1].push_back(node);
            for (int i=1;i<s;i++)
            {
                e[node].push_back(node+1);
                node++;
            }
            if (rem)
            {
                e[node].push_back(node+1);
                node++,rem--;
            }
        }
        for (int i=1;i<=n;i++)
            for (int j=0;j<e[i].size();j++)
            printf("%d %d\n",i,e[i][j]);
    }
    return 0;
}

G.Soldier and Number Game(贪心,数论)

题意:
一名士兵选择数n,n可表示为a!/b!,a、b都是正数,另一名士兵每次可以选择一个可以被n整除的数x,让n=n/x,最终使得n=1,选择的数字数量就是士兵的得分,问最大得分为多少。

题解:
题目要求要除以尽可能多的数,则除数必然为质数。将a!/b!中的所有数字进行质因数分解,各个质因数的指数相加即为答案。若每个数都进行分解会超时,我们可以用欧拉筛法预处理出 5*1e6 以内的质数 prime[number] 以及 对i进行质因数分解后得到的各个质因数指数和 sum[i]
sum[i] = sum[i/prime[j]] + 1;
prime[j] 为 i 可以整除的最小质因数

题目链接:http://codeforces.com/problemset/problem/546/D

代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define For(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;

const int maxn = 5*1e6+10;
int t,a,b,number,prime[maxn],p[maxn],ans[maxn];
bool isprime[maxn];

void getprime()
{
    For (i,2,maxn) isprime[i] = 1;
    For (i,2,maxn)
    {
        if (isprime[i])
            prime[number++] = i;
        For (j,0,number)
        {
            if (i*prime[j] > maxn) break;
            isprime[i*prime[j]] = 0;
            if (i % prime[j] == 0) break;
        }
    }
    return;
}

void solve()
{
    ans[1] = 0;
    For (i,2,maxn)
    {
        if (isprime[i]) p[i] = 1;
        else
        {
            For (j,0,number)
                if (i % prime[j] == 0)
                {
                    p[i] = p[i/prime[j]] + 1;
                    break;
                }
        }
        ans[i] = ans[i-1] + p[i];
    }
    return;
}

int main()
{
    getprime();
    solve();
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d %d",&a,&b);
        printf("%d\n",ans[a] - ans[b]);
    }
    return 0;
}

H. Kyoya and Colored Balls(排列组合)

题意:
n种颜色的球装在同一个袋子里(相同颜色的球不可分辨),每次从中抽出一个球,直到袋子里没有球。在抽球时会出现一种情况,在颜色为i+1的球被全部抽走前,已经抽走了所有颜色为i的球,问该事件发生的情况有多少种?

题解:
设所有球的个数为M,从颜色编号最大的球开始进行排列组合,则第M个被抽出的球必然为k,剩余的编号为k的球在前M-1个位置选择a[k]-1个位置;颜色为k-1的球选择一颗排在最后的空位,剩余a[k-1]-1颗球在剩余位置进行选择,以此类推……各种颜色的球排列的可能性相乘即为答案。预处理出C(i,j)

题目链接:http://codeforces.com/problemset/problem/553/A

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
using namespace std;

const int mod = 1e9 + 7;
long long f[1005][1005];
int k,tot,num[1005];

void Cij()
{
    f[0][0] = 1;
    for (int i=1;i<=1000;i++)
    {
        f[i][0] = f[i][i] = 1;
        for (int j=1;j<i;j++)
            f[i][j] = (f[i-1][j-1] + f[i-1][j]) % mod;
    }
    return;
}

int main()
{
    Cij();
    scanf("%d",&k);
    for (int i=1;i<=k;i++)
    {
        scanf("%d",&num[i]);
        tot += num[i];
    }
    long long ans = 1;
    for (int i=k;i>=1;i--)
    {
        if (num[i] == 0) continue;
        ans = (ans * f[tot-1][num[i]-1]) % mod;
        tot -= num[i];
    }
    printf("%I64d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值