質問の種類:
1. 思考問題・雑問題:数式、問題の意味分析、パターン発見
2. BFS/DFS: ワイド検索 (再帰的実装)、ディープ検索 (deque 実装)
3. 簡単な整数論:法、素数(int(sqrt(n))+1まで判定するだけ)、gcd、lcm、高速べき乗(ビット演算シフト演算)、大数分解(素数の積への分解)数字)
4. 単純なグラフ理論: 最短パス (1 対多 (Dijstra、隣接テーブル、行列の実装)、多対多 (Floyd、行列の実装))、最小スパニング ツリー (および検索セットの実装)
5. 単純な文字列処理: リスト操作に切り替えるのが最善です
6. DP: 線形 DP、最長共通部分列、0/1 ナップザック問題、最長連続文字列、最大増加部分文字列
7. 基本アルゴリズム: バイナリ、グリーディ、組み合わせ、順列、プレフィックス合計、差分
8. 基本的なデータ構造: キュー、セット、辞書、文字列、リスト、スタック、ツリー
9. よく使用されるモジュール: math、datetime、sys での最大再帰深さの設定 (sys.setrecursionlimit(3000000))、collections.deque (キュー)、itertools.combinations (list, n) (組み合わせ)、itertools.permutations (list) 、n) (順列) heapq (スモールトップヒープ)
目次
9.「最適クリアリングスキーム」実問演習(暴力、線分ツリー)
1. ペーパーナイフ(思考)
カット数は確実!ルールを見つけて出力を印刷するだけです。
2. 整数を求める
import sys #设置递归深度
import collections #队列
import itertools # 排列组合
import heapq #小顶堆
import math
sys.setrecursionlimit(300000)
import functools # 自定义比较函数 -1不变,1交换
def lcm(x,y):
return x//math.gcd(x,y)*y
x=3
step=1
b=[0,0,
1,2,1,4,5,4,1,2,9,0,5,10,
11,14,9,0,11,18,9,11,11,15,17,9,
23,20,25,16,29,27,25,11,17,4,29,22,
37,23,9,1,11,11,33,29,15,5,41,46
]
'''
3 5 7 8 9 11 13 15 17 19 21 23 25 27 2递增
5 8 11 14 17 20 23 26 29 32 3递增筛选 5 11 17 23
5 9 13 17 21 25 29 33 4递增 5 17 29
'''
for i in range(2,50): # 类似埃式筛法
while x %i !=b[i]:
x+=step
step=lcm(step,i) # 更新步长
print(x)
法を見つけるための以前のいくつかによると、暴力的な移動または法を見つける
3.「素因数の数」実問題演習(大数の分解)
標準スケジュール:
n = int(input())
ans = 0
#从2开始进行质因子分解
i = 2
while i * i <= n:
if n % i == 0:
ans += 1
while n % i == 0:
n //= i
i += 1
if n != 1:
ans += 1
print(ans)
from random import randint
from math import gcd
def witness(a, n):
u = n - 1
t = 0
while u % 2 == 0:
u = u // 2
t += 1
x1 = pow(a, u, n)
for i in range(1, t + 1):
x2 = x1 * x1 % n
if x2 == 1 and x1 != 1 and x1 != n - 1:
return True
x1 = x2
if x1 != 1:
return True
return False
#miller_rabin素性测试 对数字n进行s次测试
def miller_rabin(n, s = 5):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
for i in range(s):
a = randint(1, n - 1)
if witness(a, n):
return False
return True
#返回一个因子,不一定是素因子
def pollard_rho(n):
i, k = 1, 2
c = randint(1, n - 1)
x = randint(0, n - 1)
y = x
while True:
i += 1
x = (x * x + c) % n
d = gcd(abs(x - y), n)
if d != 1 and d != n:
return d
if y == x:
return n
if i == k:
y = x
k = k * 2
factor = []
#找所有的素因子
def findfac(n):
if miller_rabin(n):
factor.append(n)
return
p = n
while p >= n:
p = pollard_rho(p)
findfac(p)
findfac(n // p)
n = int(input())
findfac(n)
print(len(set(factor)))
4.「長方形モザイク」の実際の実践(列挙型走査)
4面、6面、8面の場合を順番に考えて数え上げて横断する
標準スケジュール:
T = int(input())
while T != 0:
T -= 1
a = list(map(int, input().split()))
a = [[a[0],a[1]], [a[2],a[3]], [a[4],a[5]]]
ans = 8
#枚举第一个矩形下标为i,第二个矩形下标为j,第三个矩形下标为k
for i in range(3):
for j in range(3):
for k in range(3):
if i == j or i == k or j == k:
continue
#枚举三个矩形的两条边
for ii in range(2):
for jj in range(2):
for kk in range(2):
if a[i][ii] == a[j][jj]:
ans = min(ans, 6)
if a[i][ii] == a[k][kk]:
ans = min(ans, 4)
if a[i][ii] == a[j][jj] + a[k][kk]:
ans = min(ans, 6)
if a[j][1 - jj] == a[k][1 - kk]:
ans = min(ans, 4)
print(ans)
5.「エリミネーションゲーム」(暴力の連鎖)
暴力的なサイクル、1 ラウンドをスキャン、マージン文字を確認、添字を記録、スキャン後に削除、完了後にサイクルを継続、終了条件: 現在の文字が空であるか、1 サイクル後も長さが変化しません。
標準スケジュール:
s = list(input())
last_length = 0
while True:
length = len(s)
#如果长度等于0,终止
if length == 0:
print("EMPTY")
break
#如果长度未发生变化,终止
if length == last_length:
print("".join(s))
break
vis = [0] * length
#根据题意找出边缘字符
for i in range(length):
if (i - 1) >= 0 and (i + 1) < length and s[i] == s[i - 1] and s[i] != s[i + 1]:
vis[i] = vis[i + 1] = 1
if (i - 1) >= 0 and (i + 1) < length and s[i] != s[i - 1] and s[i] == s[i + 1]:
vis[i] = vis[i - 1] = 1
#将边缘字符去除
tmp_s = []
for i in range(length):
if vis[i] == 0:
tmp_s.append(s[i])
s = tmp_s
last_length = length
6. 並べ替え (差分配列、貪欲)
暫定的なアイデア: 再クエリされた間隔を記録して交差があるかどうかを確認し、交差間隔を最大値に置き換えます。
差分配列
肯定的な解決策: 間隔を読み取り、間隔の訪問数 (差分配列によって実現) をマークし、貪欲なアイデアに従って、訪問数が最も多い位置に大きな値を置きます。
標準スケジュール:
import sys #设置递归深度
import collections #队列
import itertools # 排列组合
import heapq #小顶堆
import math
sys.setrecursionlimit(300000)
import functools # 自定义比较函数 -1不变,1交换
# 总体思路:查询最多的那一个放最大值,不需要序号,只需要记录最大次数
n = int(input())
a = list(map(int,input().split()))
a=[0]+a
b=[0]*(n+10)
s=[0]*(n+1)
m=int(input())
for i in range(m):
# 差分数组实现区间加法更新
l,r = map(int,input().split())
b[l]+=1
b[r+1]-=1
#对差分数组前缀和,得到每个数字的查询次数
for i in range(1,n+1):
s[i]=s[i-1]+b[i]
# sum1为原始和,sum2为贪心后的最大值
sum1,sum2=0,0
for i in range(1,n+1):
sum1+=a[i]*s[i]
# 贪心思想,大对大,小对小
a.sort()
s.sort()
# 计算重新排序后的
for i in range(1,n+1):
sum2+=a[i]*s[i]
print(sum2-sum1)
7.「完全順列の価値」練習問題(数学の定理、考え方)
予備的な考え:パターンを探していますか? ルールがない場合は、すべての順列をリストし、暴力を繰り返し、できるだけ多くのポイントを獲得してください。
法則を見つけてデータの 20% を正しく取得する
import sys #设置递归深度
import collections #队列
import itertools # 排列组合
import heapq #小顶堆
import math
sys.setrecursionlimit(300000)
import functools # 自定义比较函数 -1不变,1交换
#4 0+1*3+2*3+3*3+4*3+5*3+6
#3 0+1+1+2+2+3
#2 0+1
ans=0
n = int(input())
for i in range(1,n*(n-1)//2):
ans+=(n-1)*i%998244353
print((ans+n*(n-1)//2)%998244353)
肯定的な解: 順序数と順序数を反転したものとの合計は n*(n-1)//2、順序数と順序数を反転したものは等しく、n 個の数値には n という共通点があります。フル配列、つまり順序と逆順の合計は n!*n*(n-1)//2 なので、値の合計は上記の式を 2 で割ったものになります。
標準スケジュール:
mod = 998244353
n = int(input())
ans = n * (n - 1) // 2 % mod
for i in range(3, n + 1):
ans = ans * i % mod
print(ans)
8. 「最長の非減少部分列」の実際の実践 (DP)
暫定的なアイデア: DP アルゴリズム、つまり最長の増分サブシーケンス テンプレートを使用してこれを実行し、最長のサブシーケンスの添え字を記録し、DP 配列を使用して後ろから前まで検索して走査し、存在するかどうかを確認します。要素が最長のサブシーケンスより小さく、間隔が K より大きい場合、直接 +k?
問題があるはず、この考え方には問題がある!!すべてのデータを渡すことは不可能であり、検討は不完全です。
肯定的な解決策: DP 最長増分テンプレートを使用するというアイデアは似ていますが、線分ツリー テンプレートを使用して維持する必要があるため、この質問には満点を付けずにスキップしてください。
9.「最適クリアリングスキーム」実問演習(暴力、線分ツリー)
暫定的なアイデア:最初に操作 2 を選択し、次に操作 1 では暴力的なサイクルで十分です
肯定的な解決策:考え方は同じですが、私の考えではすべてのデータを通過することはできず、線分ツリーで処理する必要がありますが、線分ツリーを学習していません。
maxn = 1000000 + 10
tree_mi = [0] * (maxn * 4)
tree_add = [0] * (maxn * 4)
n, k = list(map(int, input().split()))
a = list(map(int, input().split()))
a = [0, *a]
#线段树模板
#利用左右儿子信息更新节点o
def push_up(o):
tree_mi[o] = min(tree_mi[o << 1], tree_mi[o << 1 | 1])
#利用节点o的lazy标记add更新左右儿子
def push_down(o):
if tree_add[o] != 0:
tree_add[o << 1] += tree_add[o]
tree_mi[o << 1] += tree_add[o]
tree_add[o << 1 | 1] += tree_add[o]
tree_mi[o << 1 | 1] += tree_add[o]
tree_add[o] = 0
#建树
def build(o, l, r):
tree_add[o] = 0
if l == r:
tree_mi[o] = a[l]
return
mid = (l + r) >> 1
build(o << 1, l, mid)
build(o << 1 | 1, mid + 1, r)
push_up(o)
#查询区间[L,R]的最小值
def query(o, l, r, L, R):
if L <= l and r <= R:
return tree_mi[o]
push_down(o);
mid = (l + r) >> 1
ans = 1000000000;
if L <= mid:
ans = min(ans, query(o << 1, l, mid, L, R))
if R > mid:
ans = min(ans, query(o << 1 | 1, mid + 1, r, L, R))
return ans
#区间更新[L,R]统一加上val
def update(o, l, r, L, R, val):
if L <= l and r <= R:
tree_mi[o] += val
tree_add[o] += val
return
push_down(o);
mid = (l + r) >> 1
if L <= mid:
update(o << 1, l, mid, L, R, val)
if R > mid:
update(o << 1 | 1, mid + 1, r, L, R, val)
push_up(o);
build(1, 1, n)
ans = 0
for i in range(1, n - k + 2):
#查询区间[i, i+k-1]的最小值
mi = query(1, 1, n, i, i + k - 1)
if mi == 0: #无法进行区间消除
#res表示当前的a[i]
res = query(1, 1, n, i, i)
#把当前的a[i]置为0
update(1, 1, n, i, i, -res)
ans += res
else:
ans += mi
#区间消除
update(1, 1, n, i, i + k - 1, -mi)
#res表示当前的a[i]
res = query(1, 1, n, i, i)
#把当前的a[i]置为0
update(1, 1, n, i, i, -res)
ans += res
for i in range(n - k + 2, n + 1):
ans += query(1, 1, n, i, i)
print(ans)
10. 「数字の分割」の実際の練習
暫定的なアイデア:大きな数の分解ができるはずです。2 つの素数または 1 つの素数に分解できるかどうかを確認します。分解できる場合は、yes を出力し、そうでない場合は、no を出力します。
import os
import sys
import math
# 请在此输入您的代码
t= int(input())
def check(n):
count=[] # 记录有多少个素数
cur=0 # 指向列表,方便记录幂次
for i in range(2,int(math.sqrt(n))+1):
if n%i==0:
count.append(0)
if len(count)>2:
print("no")
return
while n%i==0:
n=n//i
count[cur]+=1
cur+=1 #记录下一个质数幂次
if n>1:
count.append(1)
if len(count)>2:
print("no")
return
#print(count)
if len(count)==1 and count[0]>=2:
print('yes')
return
if len(count)==2 and count[0]>=2 and count[1]>=2:
print('yes')
return
print('no')
for i in range(t):
a=int(input())
check(a)
肯定的な解決策:質問の意味が間違っているようですが、この質問が尋ねているのは、分解された素数の累乗が 2 より大きければそれでよいということです。(標準的な方法は、エスペラントふるいを使用して 4000 以内の素数をすべて取得することです。各数値を調べるには、最初に平方と立方体を判断し、次に 4000 以内の素数因数を列挙します。1 に等しい累乗がある場合、ループから直接ジャンプして no を出力します)
import os
import sys
import math
# 请在此输入您的代码
t= int(input())
def check(n):
count=0 # 记录幂次
for i in range(2,int(math.sqrt(n))+1):
if n%i==0:
while n%i==0:
n=n//i
count+=1
if(count<2):
print('no')
return
count=0 # 复位,记数下一个质数
if n>1: # 剩下一个质数,幂次肯定为1,不满足
print("no")
return
print('yes')
for i in range(t):
a=int(input())
check(a)
標準スケジュール:
import os
import sys
import math
not_prime = [0]*4010
prime = []
# 预处理400以内的素数
for i in range(2,4001):
if not_prime[i]==0:
prime.append(i)
for j in range(2*i,4001,i): #埃式筛
not_prime[j]=1
# 判断平方数
def square_number(x):
y=int(x**0.5)
return y*y==x or (y+1)*(y+1)==x #防止精度出问题
# 判断立方数
def cubic_number(x):
y=int(x**(1/3))
return y**3==x or (y+1)**3 ==x
t = int(input())
for i in range(t):
a=int(input())
# 判断平方和立方
if square_number(a) or cubic_number(a):
print('yes')
continue
# 枚举4000以内因子
falg=True
for i in prime:
if a%i==0:
mi=0
while a%i==0:
a=a//i
mi+=1
if mi==1:
falg=False
break
if falg:
print('yes')
else:
print('no')