Educational Codeforces Round 112 题解

A. PizzaForces

有三种披萨,它们分别可以切成6片、8片、10片,制作它们分别需要15、20、25分钟。问得到 n n n片披萨最少需要多长时间。

解:7片需要30分钟
8片需要20分钟
9片需要35分钟
10片需要25分钟
11片需要40分钟
12片需要30分钟
13片需要35分钟
14片需要35分钟
15片需要40分钟
16片需要40分钟

6、8、10和偶数有以下规律

6810121416182022242628
68106+66+86+108+1010+1010+6+610+6+810+8+810+8+10

在10、12、14、16、18的构造中,每添加一个10,就能构造出另一组以0,2,4,6,8结尾的偶数。也就是说,大于等于10的以0,2,4,6,8结尾的偶数,都能用6、8、10构造出来。因此对于奇数,要多用一个6。

结论:如果 n n n是偶数,需要 n ∗ 2.5 n*2.5 n2.5 分钟,否则需要 ( n − 1 ) ∗ 2.5 + 15 (n-1)*2.5+15 (n1)2.5+15分钟。

#coding=utf-8
from decimal import *
DEBUG = 0

if DEBUG==1:
    data = open('sample1.in', 'r')

def readline():
    if DEBUG == 1:
        return data.readline().strip('\r\n')
    else:
        try:
            return input().strip('\r\n')
        except EOFError:
            return ''

def readi():
    return [int(t) for t in readline().split()]

t, = readi()
for _ in range(t):
    n, = readi()
    ans = 0
    if n<6:
        ans = 15
    else:
        if n%2==0:
            ans = n*Decimal(2.5)
        else:
            ans = (n+1)*Decimal(2.5)
    print(int(ans))

if DEBUG==1:
    data.close()

B. Two Tables

给定两个矩形,求最小的移动距离,使得两个矩形不相交。

#coding=utf-8
import math
DEBUG = 0

if DEBUG==1:
    data = open('sample2.in', 'r')

def readline():
    if DEBUG == 1:
        return data.readline().strip('\r\n')
    else:
        try:
            return input().strip('\r\n')
        except EOFError:
            return ''

def readi():
    return [int(t) for t in readline().split()]

t, = readi()
for id in range(t):
    #print('#'+str(id))
    W,H = readi()
    x1,y1,x2,y2 = readi()
    w = x2-x1
    h = y2-y1
    w1,h1 = readi()
    if w+w1>W and h+h1>H:
        print(-1)
        continue
    d = 2**32
    if (x1 >= w1 or y1 >= h1) or (H-y2>=h1 or W-x2>=w1):
        d = 0
    else:
        if h+h1 <= H:
            t=h1-y1
            d = min(d, t)
        if w+w1 <= W:
            t=w1-x1
            d = min(d, t)
        if h+h1 <= H and w+w1 <= W:
            t = math.sqrt((1.0*w1-x1)**2+(1.0*h1-y1)**2)
            d = min(d,t)

        cx = W-w1
        cy = H-h1
        if h+h1 <= H:
            t = y2-cy
            d = min(d,t)
        if w+w1 <= W:
            t = x2-cx
            d = min(d,t)
        if h+h1 <= H and w+w1 <= W:
            t = math.sqrt((1.0*cx-x2)**2+(1.0*cy-y2)**2)
            d = min(d,t)
        
    print(d)

if DEBUG==1:
    data.close()

C. Coin Rows

给定二维方格,alice和bob将从左上角移动到右下角,alice会取走沿途的数字,使bob能取到的数字之和尽可能地小。

解:假设alice从第一行出发一直向右直接某处向下再向右直到终点。一旦bob决定从第一行出发,那么他能取到的数字之和是第一行剩下的数字,使用前缀和来计算。 一旦bob决定从第二行出发,那么他能取到的数字之和是第二行开头的那些数字,使用后缀和来计算。所以alice只需要选择一个合适的拐点,就能使bob取到的数字之和尽可能地小。

#coding=utf-8
DEBUG = 0

if DEBUG==1:
    data = open('sample1.in', 'r')

def readline():
    if DEBUG == 1:
        return data.readline().strip('\r\n')
    else:
        try:
            return input().strip('\r\n')
        except EOFError:
            return ''

def readi():
    return [int(t) for t in readline().split()]

t, = readi()
for _ in range(t):
    m, = readi()
    a = [[] for i in range(2)]
    a[0] = readi()
    a[1] = readi()
    sum = [[0] * m for i in range(2)]
    for i in range(m):
        if i==0:
            sum[0][i] = a[0][i]
        else:
            sum[0][i] = a[0][i] + sum[0][i-1]
    for i in range(m-1,-1,-1):
        if i==m-1:
            sum[1][i] = a[1][i]
        else:
            sum[1][i] = a[1][i] + sum[1][i+1]
            
    maxi = 2**32
    for i in range(m): #alice会尽可能地压低bob能取的最大值
        bob1 = sum[0][m-1]-sum[0][i]
        bob2 = sum[1][0] - sum[1][i]
        if max(bob1,bob2) < maxi:
            maxi = max(bob1,bob2)
        
    print(maxi)
    
if DEBUG==1:
    data.close()

D. Say No to Palindromes

对于一个字符串 s s s,如果 s s s不存在长度为2以上的回文子串,则称 s s s是一个美丽的串。给定 m m m个询问,问将对应的子串变为美丽的串的最小代价是多少。

解:美丽的串只能是以下这六种形式:
abcabcabc…
acbacbacb…
bacbacbac…
bcabcabca…

将s依次与这六种形式进行比较,如果第i个字符需要修改,那么标记为1,利用前缀和可以计算出将一个子串变化为对应形式需要修改的字符数,在六种形式中取最小的那个。

n,m = readi()
s = readline()
summ = [[0] * n for i in range(6)]
k = 0
for i in itertools.permutations('abc',3):
    t = ''.join(i)
    for i in range(n):
        if s[i] != t[i%3]:
            summ[k][i] = 1
        if i>0:
            summ[k][i] += summ[k][i-1]
    k += 1

注意:如果每处理一个询问就打印一次答案会超时。如果将答案保存在ans列表里再统一输出则刚刚卡在2s左右。

#coding=utf-8
import itertools

DEBUG = 0

if DEBUG==1:
    data = open('sample1.in', 'r')

def readline():
    if DEBUG == 1:
        return data.readline().strip('\r\n')
    else:
        try:
            return input().strip('\r\n')
        except EOFError:
            return ''

def readi():
    return [int(t) for t in readline().split()]

n,m = readi()
s = readline()
summ = [[0] * n for i in range(6)]
k = 0
for i in itertools.permutations('abc',3):
    t = ''.join(i)
    for i in range(n):
        if s[i] != t[i%3]:
            summ[k][i] = 1
        if i>0:
            summ[k][i] += summ[k][i-1]
    k += 1
ans = [200000] * m
for i in range(m):
    l,r = readi()
    l -= 1
    r -= 1
    for j in range(6):
        if l-1>=0:
            ans[i] = min(ans[i], summ[j][r] - summ[j][l-1])
        else:
            ans[i] = min(ans[i],summ[j][r])
            
print(*ans,sep='\n')
    
if DEBUG==1:
    data.close()

E. Boring Segments

给定一些线段以及它们的权重,找出一组覆盖1到m的线段(相互之间要有重叠部分),且权重的极差最小。

解:将线段按权重从小到大排序。由于题目要求线段之间相互有重叠部分,对于覆盖点l到点r的线段,将r-1,这样可以将线段由覆盖哪些点转换为覆盖哪些线段。使用线段树维护被覆盖的线段,如果树根的最小值大于0,说明点1到点m被完全覆盖。使用双指针,依次将右指针指向的线段加入到线段树中直到树根的最小值大于0,此时点1到点m被完全覆盖。依次将左指针指向的线段在树根最小值大于0的情况下从线段树中移除,这样可以找到所有的能覆盖点1到点m的组合,同时能找到最小的极差。

import sys,math

DEBUG = 0

if DEBUG==1:
    sys.stdin = open('1.in', 'r')
    #sys.stdout = open('tmp.out', 'w')
def readi():
    return [int(t) for t in input().split()]

class Seg:
    def __init__(self,l,r,w):
        self.l=l
        self.r=r
        self.w=w

class Interval:
    def __init__(self,l,r):
        self.l,self.r = l,r
        self.tag = self.minv = 0
        if l==r:
            return
        mid = (l+r)//2
        self.lc = Interval(l,mid)
        self.rc = Interval(mid+1,r)
        self.update()

    def down(self):
        if self.tag != 0:
            self.lc.minv += self.tag
            self.lc.tag += self.tag
            self.rc.minv += self.tag
            self.rc.tag += self.tag
            self.tag = 0
                
    def update(self):
        self.minv = min(self.lc.minv, self.rc.minv)

    def change(self,A,B,v):
        if self.l>B or self.r<A:
            return
        if self.l>=A and self.r<=B:
            self.minv += v
            self.tag += v
            return
        self.down()
        self.lc.change(A,B,v)
        self.rc.change(A,B,v)
        self.update()

def solve(n,m,a):
    a.sort(key=lambda seg:seg.w)
    root = Interval(1,m-1)
    lp=rp=0
    ans=1000000
    while True:
        if root.minv==0: #没有被完全覆盖
            if rp<n:
                root.change(a[rp].l,a[rp].r-1,1) #将覆盖第l到第r个点转化为覆盖第l到第r-1条线段
                rp+=1
            else:
                break
        else:
            if lp<=rp-1: #rp这时候指向下一个元素,所以这里要减1
                d = a[rp-1].w - a[lp].w
                ans=min(ans,d)
                root.change(a[lp].l,a[lp].r-1,-1)
                lp += 1
    print(ans)

def main():
    n,m = readi()
    a = []
    for i in range(n):
        l,r,w = readi()
        a.append(Seg(l,r,w))
    solve(n,m,a)

main()
#include<bits/stdc++.h>
using namespace std;

class Interval
{
public:
	Interval *lc,*rc;
	int l,r,minv,tag;
	Interval(int l, int r)
	{
		this->l=l;this->r=r;
		tag=minv=0;
		lc = rc = NULL;
		if(l==r)
			return;
		int mid=(l+r)/2;
		lc=new Interval(l,mid);
		rc=new Interval(mid+1,r);
	}
	void down()
	{
		if(tag!=0)
		{
			lc->minv+=tag;
			lc->tag+=tag;
			rc->minv+=tag;
			rc->tag+=tag;
			tag=0;
		}
	}
	void update()
	{
		minv=min(lc->minv,rc->minv);
	}
	void change(int A, int B, int v)
	{
		if(l>B || r<A)
			return;
		if(l>=A && r<=B)
		{
			minv += v;
			tag += v;
			return;
		}
		down();
		lc->change(A,B,v);
		rc->change(A,B,v);
		update();
	}
};

struct Seg
{
	int l,r,w;
	bool operator<(const Seg& s1)
	{
		return w<s1.w;
	}
} seg[300001];

int main()
{
	//freopen("1.in", "r", stdin);
	
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0; i<n; i++)
    {
    	scanf("%d%d%d",&seg[i].l,&seg[i].r,&seg[i].w);
	}
	std::sort(seg,seg+n);
	Interval root(1,m-1);
	int lp,rp,ans;
	lp = rp = 0;
	ans = 1000000;
	while(1)
	{
		if(root.minv == 0)
		{
			if(rp<n)
			{
				root.change(seg[rp].l, seg[rp].r-1, 1);
				rp++;
			}
			else
				break;
		}
		else
		{
			if(lp <= rp-1)
			{
				int d = seg[rp-1].w - seg[lp].w;
				if(d < ans)
					ans = d;
				root.change(seg[lp].l, seg[lp].r-1, -1);
				lp++;
			}
		}
	}
	printf("%d",ans);
    return 0;
}
  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值