Codeforces Round #470 (VK Cup 2018 Round 1)

本文探讨了两个有趣的算法问题。第一个问题是保护羊群免受狼的侵扰,通过放置狗来隔离羊和狼,并确保羊的安全。第二个问题是关于整数的变换过程,寻找经过两次特定变换后得到给定值的最小起始整数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Protect Sheep: 地图上有羊有狼,需要在空白处放狗将羊和狼分隔开。

羊和狼有相邻的时候肯定搞不定,否则……只需要……把所有的空地……都放上狗。(○` 3′○)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 556
#define maxm 1050
#define INF 0x3f3f3f3f
#define eps 1e-8
const int mod = 1e6;
using namespace std;
typedef long long ll;

int n,m;
char maze[maxn][maxn];
int pos[4][2]={1,0,0,1,-1,0,0,-1};

bool judge(int x,int y)
{
    if(x>=0&&y>=0&&x<n&&y<m)
        return true;
    return false;
}

int main()
{
    scanf("%d%d",&n,&m);
    bool flag=false;
    for(int i=0;i<n;i++)
        scanf("%s",maze[i]);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(maze[i][j]=='S')
            {
                for(int k=0;k<4;k++)
                {
                    int x=i+pos[k][0];
                    int y=j+pos[k][1];
                    if(judge(x,y)&&maze[x][y]=='W')
                    {
                        flag=true;
                        break;
                    }
                }
            }
            if(flag)break;
        }
    }
    if(flag)
        printf("No\n");
    else
    {
        printf("Yes\n");
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(maze[i][j]=='.')
                    printf("D");
                else printf("%c",maze[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

B. Primal Sport:  定义对于一个>=3的整数x的变换:找出小于x的一个素数a,将x变为不小于x的a的最小的倍数。已知某正整数经两次变换后的结果,求这个数可能的最小值。

设pre(x)为x的最大素因子,则可知,当且仅当整数在[x-pre(x)+1,x]范围内时,整数的变换值为x。则可对于给出的X2,暴力枚举X1的范围[X2-pre(X2)+1,X2],找出其中X0=X1-pre(X1)+1的最小值。使用素数筛不仅可以标记非素数,还可以同时标记最大素因子。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 1000050
#define INF 0x3f3f3f3f
#define eps 1e-8
const int mod = 1e6;
using namespace std;
typedef long long ll;

int n,no;
bool vis[maxn];
int pre[maxn];
void init()
{
    no=0;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            for(int j=2*i;j<=n;j+=i)
            {
                vis[j]=1;
                pre[j]=i;
            }
        }
    }
}

int main()
{
    scanf("%d",&n);
    init();
    int tmp=pre[n];
    int ans=INF;
    for(int i=n;i>n-tmp;i--)
        ans=min(ans,i-pre[i]+1);
    printf("%d\n",ans);
    return 0;
}

C. Producting Snow:  在第i天制造一个体积为vi的雪人,同时每个雪人都会融化ti的体积,求每一天中融化的总体积。

普通的O(n^2)处理肯定是不行了。对于每一天的雪人,我们都要计算出在哪一天它会全部融化,然后将这一天的总体积加上相应的数值。遍历所有的雪人必然是O(n)的复杂度,这就要求计算和更新时间内都得在O(logn)的复杂度内完成。考虑到时间更新必然是在一个连续的时间区间里,可以维护t[i]的前缀和,对于第i个雪人二分找出它完全融化的时间k,用一个树状数组维护第i天中整天融化的雪人数,再单独维护第k天融化量小于t[i]的余数。这之中有一个问题:在n天结束时雪人并不一定会融化完。对于用lower_bound找出的k,树状数组上应更新i~k-1时间。而对于第k天,当i<=k时(这是为了避免v[i]=0的情况,v[i]=0时i会小于k,无需更新k),若v[i]+sum[i-1]<=sum[k],即在第k天可以化完,更新量cnt[k]+=(v[i]+sum[i-1]-sum[k-1]),否则就只能cnt[i]+=t[i],在第i天达到了最大融化量仍未化完。最终第i天的总融化量即为tree[i]*t[i]+cnt[i]。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
const int mod = 1e9 + 7;
using namespace std;
typedef long long ll;

int n;
ll v[maxn], t[maxn];
ll sum[maxn], cnt[maxn];
int tree[maxn];
int lowbit(int x) { return x&(-x); }

void add(int x, int w)
{
	while (x <= n)
	{
		tree[x] += w;
		x += lowbit(x);
	}
	return;
}

int query(int x)
{
	int ans = 0;
	while (x > 0)
	{
		ans += tree[x];
		x -= lowbit(x);
	}
	return ans;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%I64d", &v[i]);
	for (int i = 1; i <= n; i++)
	{
		scanf("%I64d", &t[i]);
		sum[i] = sum[i - 1] + t[i];
	}
	memset(tree, 0, sizeof(tree));
	for (int i = 1; i <= n; i++)
	{
		int k = lower_bound(sum + 1, sum + n + 1, v[i] + sum[i - 1]) - sum;
		if (i < k)add(i, 1), add(k, -1);
		if (i <= k)
		{
			if (v[i] + sum[i - 1] > sum[k])cnt[k] += t[i];
			else cnt[k] += (v[i] + sum[i - 1] - sum[k - 1]);
		}
	}
	for (int i = 1; i <= n; i++)
	{
		int tmp = query(i);
		printf(i == n ? "%I64d\n" : "%I64d ", t[i] * tmp + cnt[i]);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值