2020年常熟理工学院第一届线上ACM选拔赛题解(Python版)

本次题目难度顺序基本从难到易,在这里非常感谢出题组同学的真情付出。此博客原文地址:https://www.cnblogs.com/BobHuang/p/12610801.html

1.6195: Trojke II

这个题目是4140: Trojke的一个扩展,在这个题目里由于可以匹配的棋子很多,我们应该想的是去遍历这个棋盘的所有线。这就是格点问题:从(0,0)到(x,y)的线段,经过的格点数目是gcd(x,y)+1。比如(3,5)是两个;(2,4)就是3个,因为过了(1,2);(8,20)是5个,因为还过了(2,5)、(4,10)、(6,15)。这个的证明可以从相似三角形下手,比较简单。
所以这个题目我们预处理出所有的线就可以了,具体实现思路可以看代码。我们可以查一条线上的点的个数x,然后C(x,3)就是当前点构成三胞胎的个数。
复杂度分析:
直接三个字符求解
m * m * m 如果满数据就是n6,1006= 1012=1e12
考虑所有的斜线,就是已知100以内互质对数 * n2
100以内互质对数为6087,实际可能的是1547(优化过的)
1547 * n * n = 1e7,当然还有常数,但是足够通过这个题目了
出题人的代码为预处理欧拉,枚举所有欧拉再枚举点,用的dp转移
python实在太慢了,这个代码本地跑一个大样例要7s

import math
n = int(input())
s = [''] * n
for i in range(n):
    s[i]=input()
vis=[[False] * n for i in range(n)]
V=[]
def cal(cnt):
    if(cnt>=3):
        return cnt * (cnt - 1) * (cnt - 2) // 6
    return 0
def add(i,j,ai,aj):
    cnt = 0
    while j < n and i < n:
        vis[i][j] = True
        if s[i][j] != '.':
            cnt = cnt+1
        i = i + ai
        j = j + aj
    return cal(cnt)
def sub(i,j,ai,aj):
    cnt = 0
    while j >= 0 and i < n:
        vis[i][j] = False
        if s[i][j] != '.':
            cnt = cnt+1
        i = i + ai
        j = j - aj
    return cal(cnt)

for i in range(1,n+1):
    for j in range(1,n+1):
        if math.gcd(i, j) == 1 and min(n / i, n/ j) > 2 :
            V.append([i, j])
ans = 0
for X in V:
    for i in range(n):
        for j in range(n):
            if vis[i][j]==False:
                ans = ans + add(i, j, X[0], X[1])
    for i in range(n):
        for j in range(n):
            if vis[i][j]==True:
                ans = ans + sub(i, j, X[0], X[1])
for j in range(n):
    ans = ans + add(0, j, 1, 0)
for i in range(n):
    ans = ans + add(i, 0, 0, 1)
print(ans)

2.6197: 最好一样

这个是位运算的题目,这是“按位或”运算符,||是逻辑或,两个相应的二进制位中只要有一个为1,该位的结果值为1,即有1得1。曾经写过一个operator的理解,有兴趣可以看看。
我们可以以当前数字作为下标进行统计个数。或会让这个数字变大(有些位上多1),所以我们从小的数字开始枚举,如果或的值不为i,说明异或的新值可以多a[i]个,当前清空,如果这个数字恰巧只有一个,那只能当读出现了,统计下来。
update: 要么a[i]不或m,要么或m,如果存在显然或m,这样会减少。一个数字或上两次并不会变得更大,所以一次处理也可以。
100000 并不是一个规整的数,我们可能或为2倍(其实就是1<<17=131072,略大于是最省的),也就是你要找到大于他的二进制数

n,m=map(int,input().split())
b=map(int,input().split())
a=[0]*(1<<17)
for i in b:
    a[i] = a[i] + 1
ans=0
for i in range(1<<17):
    if (i|m)!=i and a[i]!=0 and (i|m)<(1<<17):
        a[i|m] = a[i|m] + a[i]
        a[i] = 0
    if a[i]==1:
        ans = ans + 1
print(ans)

3.6198: Alice与进制转换进阶版

非常抱歉这个题目出现了问题,影响了ydqdsg非常抱歉
py的int很大很大,我们可以直接使用,但是他没有很方便的函数,转到r2进制需要手写
你可以看看cpp的实现修改代码尝试下AC6222终极版

def dec_to_base_r(num,r):
    if num<0:
        return "-"+dec_to_base_r(-num,r)
    base = [str(x) for x in range(10)] +[chr(x) for x in range(ord('A'),ord("A")+6)]
    l = []
    while True:
        num,rem = divmod(num,r)
        l.append(base[rem])
        if num == 0:
            return "".join(l[::-1])
while True:
    try:
        n,r1,r2=map(str,input().split())
        n=int(n,int(r1))
        print(dec_to_base_r(n,int(r2)))
    except EOFError:
        break

Java写起来很容易

import java.io.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String args[]) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            String s = sc.next();
            int r1 = sc.nextInt();
            int r2 = sc.nextInt();
            String a = new BigInteger(s,r1).toString(r2);
            System.out.println(a.toUpperCase());
        }
    }
}

4.6194: jump jump jump

这个题目可以广搜解答,广搜可以保证每次搜到的都是最近的,且每个点只会被访问一次
当然也可以记忆化搜索。
当然也可以dp解答,因为每个点只访问一次,而且最小
dp[i]代表i~n的最小步数。

n,k = map(int,input().split())
a = list(map(int,input().split()))
dp = [int(1e9)]*(n+1)
dp[n] = 1
for i in range(n,0,-1):
    if(i+a[i-1]<=n):
        dp[i]=min(dp[i+a[i-1]]+1,dp[i])
minn=int(1e9)
for i in range(k):
    minn=min(minn,dp[i+1])
if(minn==int(1e9)):
    print(-1)
else:
    print(minn)

5.6220: Alice与函数图像

这个题目略微困难,y= - x³ - bx和 y = x³ + bx是等价的,因为他们的函数图像是对称的,x1³ + bx1 - x2³ + bx,有立方差公式(a-b)(a²+ab+b²)=a³-b³,以上进行合并为 ( X1 - X2 ) * ( X1 * X1 + X1 * X2 + X2 * X2 + b)
update:感谢liqiyao0430hack了标程,时间仓促,出题人没有考虑到(写的不等式错了)。
1.后面部分为1,假设(X1-X2)为素数P,后面为1,X1=P+X2
代入得到3X2 * X2+3PX2 +P*P+b为二次函数,开口向上对称轴为-2/P,最低点为

\[(-\frac{P}{2}\ ,\frac{P^2}{4}+b) \]

P=2且b=0,最小值为1,X1=1,X2=-1,所以这个需要特判掉。
2.前面部分为1,X1-X2=1代入可得
是对函数3 * i * i + 3 * i = p +1 -b存在解,当然也可以直接二分,也可以判断根。判断根会超过ll,需要unsiged,当然你也可以给他进行因式分解为3i * (i+1)= = p+1-b
(p-1-c)%3 == 0 and int(sqrt((p-1-c)/3)) * (int(sqrt(p-1-c)/3))+1)==(p-1-c)/3

import math
while True:
    try:
        b,p=map(int,input().split())
        f=int(math.sqrt((p-1-b)/3))
        if b==0 and p==2:
            print('Existent')
        elif (p-1-b)%3 == 0 and f * (f+1)==(p-1-b)//3:
            print('Existent')
        else :
            print('Non-existent')
    except EOFError:
        break

6.6193: Alice玩16点

我们可以对24点的代码进行改造,我们可以判断是是不是有4和9然后进行变换就可以了。
当然也可以带上flag搜,dfs就是这样,暴力和好写的一个平衡。

from itertools import permutations
import math
def la(s):
    if s=='A':
        return 1
    if s=='JOKER':
        return 0
    if s=='J':
        return 11
    if s=='Q':
        return 12
    if s=='K':
        return 13
    return int(s)
f = 0
def dfs(pos,flag,sum):
    global f
    if f==1:
        return
    if pos==3:
        if sum==16:
            f=1
        return
    if pos==0:
        if a[pos]==4 or a[pos]==9 :
            dfs(pos+1,0,int(math.sqrt(a[pos])))
        dfs(pos+1,1,a[pos])
    else:
        if (a[pos]==4 or a[pos]==9) and flag==1:
            dfs(pos+1,0,sum*int(math.sqrt(a[pos])))
            dfs(pos+1,0,sum//int(math.sqrt(a[pos])))
            dfs(pos+1,0,sum+int(math.sqrt(a[pos])))
            dfs(pos+1,0,sum-int(math.sqrt(a[pos])))
        dfs(pos+1,flag,sum*a[pos])
        if a[pos]!=0:
            dfs(pos+1,flag,sum//a[pos])
        dfs(pos+1,flag,sum+a[pos])
        dfs(pos+1,flag,sum-a[pos])
T=int(input())
for i in range(T):
    f=0
    tmp1=map(str,input().split())
    tmp2=[la(i) for i in tmp1]
    b = permutations(tmp2)
    for a in b:
        dfs(0,1,0)
    if f==1: 
        print('YES')
    else:
        print('NO')

7.6202: 有趣的活动

排成圈,其实就是扩展一次。所以可以边扩展边记录,找到最大值即可。
这个题目很多人被卡超时,如果使用C++请关闭输入输出同步,尽量使用scanf和printf。不要混用。如果使用JAVA你可以去codeforces看下peter的代码,把它的Java读入抄下来。
这个自己也可以百度,也是考点,如果还不能通过就要考虑使用快读

def la():
    n = int(input())
    a = list(map(float,input().split()))
    a = a+a 
    dp = [0]*(n+n)
    dp[0] = 1
    for i in range(1,n+n):
        if a[i] <= a[i-1]:
            dp[i] = dp[i-1]+1
        else: 
            dp[i]=1
    ans=0
    for i in range(n+n):
        ans=max(ans,min(dp[i],n))
    print(ans)
T=int(input())
for i in range(T):
    la()

8.6196: Alice玩五子棋

这个题目可以循环判断,也可以直接搜索

n=15
def la(c):
    for i in range(11):
        for j in range(11):
            if s[i][j]==c and s[i][j]==s[i+1][j+1] and s[i][j]==s[i+2][j+2] and s[i][j]==s[i+3][j+3] and s[i][j]==s[i+4][j+4] or s[i][n-j-1]==c and s[i][n-j-1]==s[i+1][n-j-2] and s[i][n-j-1]==s[i+2][n-j-3] and s[i][n-j-1]==s[i+3][n-j-4] and s[i][n-j-1]==s[i+4][n-j-5]:
                return True
    for i in range(n):
        for j in range(11):
            if s[i][j]==c and s[i][j]==s[i][j+1] and s[i][j]==s[i][j+2] and s[i][j]==s[i][j+3] and s[i][j]==s[i][j+4]:
                return True
    for i in range(11):
        for j in range(n):
            if s[i][j]==c and s[i][j]==s[i+1][j] and s[i][j]==s[i+2][j] and s[i][j]==s[i+3][j] and s[i][j]==s[i+4][j]:
                return True
    return False

while True:
    try:
        s = [''] * 15
        for i in range(15):
            s[i]=input()
        if la('X'):
            print("Bob Wins!")
        elif(la('O')):
            print("Alice Wins!")
        else:
            print("continue")
    except EOFError:
        break

9.6221: taozi与一元一次方程

这个就是方程的判断,y=ax+b,斜率为无穷对应无数个解,如果a=0,且y!=b是无解

while True:
    try:
        y,a,b=map(int,input().split())
        if a==0 :
            if y==b :
                print("Infinite")
            else :
                print("Unsolvable")
        else :
            print('%.2f' %((y-b)/a))
    except EOFError:
        break

10.6191: Alice与分数序列

python可以直接除,默认就是得到小数

while True:
    try:
        a,b,c,d=map(int,input().split())
        if a/b > c/d:
            print(a,'/',b,'>',c,'/',d)
        elif a/b < c/d:
            print(a,'/',b,'<',c,'/',d)
        else:
            print(a,'/',b,'=',c,'/',d)
    except EOFError:
        break

11.6203: Alice与三角形面积

可以直接勾股定理,也可以相似(两小三角形相似,另一直角边为(b*a/c)),而且怎么AC都能对。勾股数恰好满足可以凑答案,甚至正好整除。
这个题错误的是因为多组数据。

while True:
    try:
        a,b,c=map(int,input().split())
        print('%.3f' %(a*a*b*1.0/2/c))
    except EOFError:
        break
posted @ 2020-04-02 18:04  暴力都不会的蒟蒻  阅读(645)  评论(0编辑  收藏  举报