自我介绍

2010~2013 石家庄二中
NOI2012 金牌
2013~2017 清华大学
期间5次ICPC金牌
2017~2019 算法竞赛
2019~现在 量化交易

这个课讲什么

  1. 不是玩游戏
  2. 不是修电脑,修手机
  3. 不是写网站,写手机APP
  4. 不是黑客

是做编程题

数学题和编程题对比

数学题

一个正方形边长是2米,这个正方形的面积是______

编程题

输入一个正方形的边长n
输出这个正方形的面积

输入格式
一行一个整数 n

输出格式
一行一个整数表示正方形的面积

样例输入
2

样例输出
4

数据范围
1 <= n <= 10

编程题的特点

客观:不受判卷老师心情影响
黑盒:只关注输入和输出
得分点:只看做对几组数据

竞赛的路径

中国的比赛

基本都离不开 CCF China Computer Federation 中国计算机学会
https://www.noi.cn/

https://www.noi.cn/xw/2022-02-25/756122.shtml

  1. CSP 入门级
  2. CSP S1提高级
  3. CSP S2提高级
  4. NOIP
  5. 省选
  6. NOI (决定谁可以不参加高考上大学)
  7. 冬令营
  8. CTSC

https://ti.luogu.com.cn/problemset/1034

世界的比赛

IOI (International Olympiad in Informatics)
五大学科竞赛之一(其他四个学科竞赛是 数学 物理 化学 生物)

实际上,国际上还有语言学,地理学等比赛
但是在中国不能升学的比赛都是爱好

各国的比赛

USACO (美国)
JOI (日本)
...

地位大致相当于中国的NOIP ~ NOI
用来选拔国家队(谁来参加IOI)

大学生的比赛

ICPC(世界的)
CCPC(中国的)

商业比赛

中国的

百度之星(百度)
计蒜之道(美团)

外国的

Google Code Jam
Facebook Hacker Cup
Topcoder Open

每周都会有的比赛

Atcoder
Codeforces
LeetCode
Luogu

开始学习的时间点

二年级学 Python
四年级学 C++

我们这里的课程设置

时代班
学习目标:基本语法
输入输出
判断
循环
数组

高度班
学习目标:基本算法
判断质数
计算约数和
排序

队列

为什么学编程

升学

找工作

怎么学编程

学好数学
很多编程题的基础是数学题

自学:
许多网站可以看其他人的代码和题解,有利于自学
https://atcoder.jp/
http://codeforces.com/
https://www.luogu.com.cn/

学习准备

  1. 安装 DEV C++
  2. 安装浏览器
  3. 在 atcoder codeforces luogu 注册账号
    注册账号需要邮箱
  4. 怎么打字

Python

看了下文档,这个函数特殊情况很多
islower 是判断字符串中是否所有字母都是小写
如果其中有大写字母就返回False
如果没有小写字母(比如只有数字符号空格)就返回False
如果没有大写字母(即使有数字符号空格)也返回True

只判断一个字符是正常的

http://usaco.org/

4天
北京时间周五20点到周二的20点
选4小时

USACO 比赛中就可以提交
提交错误没有惩罚

freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);

https://www.luogu.com.cn/

https://codeforces.com/

https://atcoder.jp/

ABCDABC
ABCD
BCDA
CDAB
DABC

ABC
BCD
CDA
DAB
ABC

若干个 int / string 去重
C++
std::set
sort unique

Java
HashSet
sort

C++ pair

sort 尤其是 class 自定义的排序 Comparable Comparator
TreeMap
TreeSet

字典序
abc
abd

ab
abc

1 1 2 2
1 2 1 2
1 2 2 1
2 1 1 2
2 1 2 1
2 2 1 1

int 4个字节

6400万个int = 256MB

n / 8 * 15 + ..

if (n % 8 == 0)
{
n / 8 * 15 - 1
}
if (n % 8 == 1)
{
n / 8 * 15 + 1
}
if (n % 8 == 2)
{
n / 8 * 15 + 2
}
....

a = {-1, 1, 2, 4, 7, 8, 11, 13};
n / 8 * 15 + a[n % 8]

染成不同的块 2类做法

  1. 并查集
  2. DFS

ArrayList a;

for (int i: a)
{

}

import sys
sys.stdin = open('word.in', 'r')
sys.stdout = open('word.out', 'w')
n, m = map(int, input().split())
l = 0
for s in input().split():
# print(l, s, len(s))
if l + len(s) > m:
print()
l = 0
if l > 0:
print(' ', end='')
print(s, end='')
l += len(s)
print()

P2161 [SHOI2009]会场预约
P1059 明明的随机数
P1503 鬼子进村
P2061 [USACO07OPEN]City Horizon S
P2073 送花
P2141 珠心算测验
P2234 [HNOI2002]营业额统计
P4404 [JSOI2010]缓存交换
P1125 笨小猴
P2814 家谱

DEVCPP

安装

修改语言

C++ 语法学习

第一个能编译的程序

从不会写程序到会写程序
刚开始妨碍你学会写程序的几个拦路虎:

  1. 做任何题之前,先写这几行
#include <bits/stdc++.h>
using namespace std;
int main()
{
    return 0;
}
  1. 每个语句结束都要有分号。
  2. 变量必须先定义再使用。
  3. main 主要的,最重要的
  4. 所有符号必须是英文半角符号,程序中不要出现中文字符。

输入输出

读入 输出

cin / cout
scanf / printf

循环 判断

数组

函数

递归 recursion

dfs()

Error: ENOENT: no such file or directory, open '/Users/wwwwodddd/Dropbox/Informatics/cincout.md'

C++的if

if语句使用注意事项:

  1. if 后括号内,写条件,括号后没有分号
  2. if 必须有,else 可有可无, else if 可以没有可以有多个
  3. 如果条件满足,运行接下来大括号包含的一段代码
  4. 如果这段代码只有一句,可以省略大括号,不建议大家这样做
  5. 代码如何运行,与缩进和空白字符没有关系
#include <bits/stdc++.h>
using namespace std;
int x; // 在 main 外的变量叫做全局变量,默认是 0
int main()
{
    if (x > 0)
        cout << "a" << endl; // 条件不满足,不会输出 a
        cout << "b" << endl; // 不在 if 中,会输出 b
    if (x > 0)
    {
        cout << "a" << endl; // 条件不满足,不会输出 a
        cout << "b" << endl; // 条件不满足,不会输出 b
    }
    if (x > 0);
    {
        cout << "a" << endl; // if 后不能加分号,如果加,会认为 if 只包含这个分号
        cout << "b" << endl; // 会输出 a 和 b,因为他们不在 if 中
    }
    if (x > 0)
        cout << "a" << endl;cout << "b" << endl;
    // 即使写在同一行,不会输出 a, 输出 b 的部分不在条件中,会输出 b
    return 0;
}

判断练习题

abc191_a Vanishing Pitch

https://atcoder.jp/contests/abc191/tasks/abc191_a
输入V,T,S,D,V是速度,D是路程,问时间是否在T和S之间,输出Yes或No

abc176_a Takoyaki

https://atcoder.jp/contests/abc176/tasks/abc176_a
做一次可以做X个零食,需要T的时间。问做N个零食需要多久?
除法上取整

abc167_a Registration

https://atcoder.jp/contests/abc167/tasks/abc167_a
输入两个字符串,问第一个是不是第二个的前缀
用substr

abc165_a We Love Golf

https://atcoder.jp/contests/abc165/tasks/abc165_a
输入K, A, B。问A和B之间有没有K的倍数

abc158_a Station and Bus

https://atcoder.jp/contests/abc158/tasks/abc158_a
输入一个长度为3的字符串,问是不是既有A,又有B

abc072_a Sandglass2

https://atcoder.jp/contests/abc073/tasks/abc072_a
输入X和t,一个沙漏,初始X的沙子,每秒漏1的沙子,问最后剩多少沙子

abc059_a Three-letter acronym

https://atcoder.jp/contests/abc059/tasks/abc059_a
输入三个单词,输出三个单词的首字母大写

abc057_a Remaining Time

https://atcoder.jp/contests/abc057/tasks/abc057_a
现在时间是A点整,问B小时之后是几点整,输出一个0到23之间的数字

abc056_a HonestOrDishonest

https://atcoder.jp/contests/abc056/tasks/abc056_a
输入两个字母,相同输出H,不同输出D

abc055_a Restaurant

https://atcoder.jp/contests/abc055/tasks/abc055_a
800日元一顿饭,每吃15顿可以获得200日元的优惠,问吃n顿花多少钱

switch

else if

循环

while

只要给定的条件为真,while 循环语句会重复执行一个目标语句。
语法

C++ 中 while 循环的语法:

while (condition)
{
    statement(s);
}

在这里,statement(s) 可以是一个单独的语句,也可以是几个语句组成的代码块。condition 可以是任意的表达式,当为任意非零值时都为真。当条件为真时执行循环。

当条件为假时,程序流将继续执行紧接着循环的下一条语句。

for

for (initialization; condition; increment)
{
    statement;
}

do while

do
{
    statement;
}
while (condition);

break 语句

终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。

continue 语句

引起循环跳过主体的剩余部分,立即重新开始测试条件。

goto 语句

将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。

无限循环

如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。
实例

#include <iostream>
using namespace std;
int main ()
{
    for(;;)
    {
        printf("This loop will run forever.\n");
    }
    return 0;
}

循环

想退出多重循环怎么办?

  1. flag变量,加很多 if break
  2. 使用函数,return结束函数
  3. 直接结束程序,return 0;,或者exit(0);

多层循环

循环练习题

abc215_b log2(N)

https://atcoder.jp/contests/abc215/tasks/abc215_b
输入n,找到最大的k,使得2**k<=n

abc207_b Hydrate

https://atcoder.jp/contests/abc207/tasks/abc207_b
输入正整数A, B, C, D,问是否存在自然数x使得 A + x * B <= x * C * D
如果存在输出最小的x,如果不存在输出-1

abc206_b Savings

https://atcoder.jp/contests/abc206/tasks/abc206_b
第1天存1块钱,第2天存2块钱……第i天存i块钱,
问多少天能存够n的钱。

abc204_b Nuts

https://atcoder.jp/contests/abc204/tasks/abc204_b
有n个树,每个树上有一些坚果,如果坚果的个数大于10,会拿到只剩10个,否则一个都不拿,问一共拿几个?

abc203_b AtCoder Condominium

https://atcoder.jp/contests/abc203/tasks/abc203_b
第i层楼,第j个房间的门牌号是i0j,一共n层楼,每层k个房间,问所有门牌号之和。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
    cin>>n>>m;
    cout<<n*(1+m)*m/2+m*(1+n)*n/2*100;
}

abc200_b 200th ABC-200

https://atcoder.jp/contests/abc200/tasks/abc200_b
对n做操作:如果n是200的倍数,/=200,否则在n的数字串末尾加上200三位数字
输入n,和k,输出n做k次操作的结果

#include<bits/stdc++.h>
int main(){
  long long n,k;
  scanf("%lld%lld",&n,&k);
  while(k--){
    if(n%200==0)n/=200;
    else n=n*1000+200;
  }
  printf("%lld\n",n);
}

abc193_b Play Snuke

https://atcoder.jp/contests/abc193/tasks/abc193_b
n个商店,走到第i个商店需要Ai分钟,价格是Pi,库存是Xi
如果Xi<=Ai这个店会在自己走到之前卖完
需要选一个有存货的商店买一个商品,问至少需要多少钱
如果买不到输出-1

abc191_b

https://atcoder.jp/contests/abc191/tasks/abc191_b

输入n,x,和一个长度为n的数组A,把A中的x都删掉,输出剩下的数字保持原序。

#include <bits/stdc++.h>
using namespace std;
int main() {int X, A; for (cin >> X >> X; cin >> A;) if (A - X) cout << A << " ";}

abc190_b Magic 3

https://atcoder.jp/contests/abc190/tasks/abc190_b
n个技能,每个技能施法时间Xi,伤害Yi,问有没有施法时间<S且伤害>D的技能

abc189_b Alcoholic

https://atcoder.jp/contests/abc189/tasks/abc189_b
n杯酒,一杯一杯喝,每一杯有容量V和浓度P,问喝到第几杯,已经喝的酒精>X

C++的数组

https://www.runoob.com/cplusplus/cpp-arrays.html

•【1】数组定义,数组与数组下标的含义
•【1】数组的读入与输出
•【2】纯一维数组的综合运用
•【3】纯二维数组与多维数组的综合应用

C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。

数组的声明并不是声明一个个单独的变量,比如 number0number1...number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]numbers[1]...numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。

所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。

声明数组

在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:

type arrayName [ arraySize ];

这叫做一维数组。arraySize 必须是一个大于零的整数常量,type 可以是任意有效的 C++ 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:

double balance[10];

现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。

常见问题

如果输入既有可能是 2*500000 的数组,也有可能是 500000*2 的数组怎么办?

  1. 开一位数组,自己算下标,比如一共n行m列,那第i行第j列可以变成一维数组的i*m+j
  2. 使用vector

数组练习题

https://atcoder.jp/contests/abc161/tasks/abc161_b
n个商品被投票,第i个的票数是Ai
问能不能选出m个商品,使得选出的商品的票数都大于等于 总票数/(4m)

数组或循环练习题

abc151_b Achieve the Goal

https://atcoder.jp/contests/abc151/tasks/abc151_b
一共N门课,每门课的分数是0到K的一个数字
已知前N-1门课的分数,问最后一门课至少多少分,才能使得所有课平均分>=M
如果做不到输出-1

abc143_b TAKOYAKI FESTIVAL 2019

https://atcoder.jp/contests/abc143/tasks/abc143_b
输入n个数字,我们都知道从n个数字中选两个有n*(n+1)/2个方案
对于这n*(n+1)/2个方案,选出的2个数字计算乘积
问这n*(n+1)/2个乘积的和是多少?

abc142_b Roller Coaster

https://atcoder.jp/contests/abc142/tasks/abc142_b
输入n个数字,问其中有多少个>=h

abc135_b 0 or 1 Swap

https://atcoder.jp/contests/abc135/tasks/abc135_b
输入一个1到n的排列,问能不能通过交换一次让排列变得有序?

abc130_b Bounding

https://atcoder.jp/contests/abc130/tasks/abc130_b
输入X和n个长度Li,初始在0,第i次向前走Li的长度,问有几次所在的位置<=X
在0不走也算一次

abc127_b Algae

https://atcoder.jp/contests/abc127/tasks/abc127_b
输入rDX[2000]
X[i+1]=r*X[i]-D
输出X[2001], ..., X[2010]

abc124_b Great Ocean View

https://atcoder.jp/contests/abc124/tasks/abc124_b
输入一个数组,问有多少个数组大于等于自己左边的所有数字

abc120_b K-th Common Divisor

https://atcoder.jp/contests/abc120/tasks/abc120_b
输入A, B, K输出ABK大的公约数,保证存在

基础语法题

abc070_b Two Switche

https://atcoder.jp/contests/abc070/tasks/abc070_b
甲在时刻A秒按下按钮,时刻B秒松开按钮
乙在时刻C秒按下按钮,时刻D秒松开按钮
问甲乙同时按下按钮多少秒?

abc134_b Golden Apple

https://atcoder.jp/contests/abc134/tasks/abc134_b
一个人如果站在i,可以监视i-d到i+d的所有人,现在1到n共n个人排成一行
问需要几个人才能监视所有人?

abc126_b YYMM or MMYY

https://atcoder.jp/contests/abc126/tasks/abc126_b
输入4位数的月份,问是YYMM格式还是MMYY格式,输出
YYMM 如果只能是 YYMM
MMYY 如果只能是 MMYY
AMBIGUOUS 如果都可能
NA 如果都不可能

abc154_a Remaining Balls

https://atcoder.jp/contests/abc154/tasks/abc154_a
输入A个S色的球,B个T色的球
拿走一个U色的球
问S色的球和T色的球还剩下几个(其中有且只有一种颜色会减少一个)

abc153_a Serval vs Monster

https://atcoder.jp/contests/abc153/tasks/abc153_a
一个怪物初始生命值H,被攻击一次生命值减少A,如果怪物生命值小于等于零玩家胜利
问需要攻击几次

abc148_a Round One

https://atcoder.jp/contests/abc148/tasks/abc148_a
三个选项1, 2, 3
输入两个错误选项,问正确的是哪个?

abc144_a 9x9

https://atcoder.jp/contests/abc144/tasks/abc144_a
输入两个数字,如果都是1~9的数字,输出他们的乘积
否则输出-1

abc122_a Double Helix

https://atcoder.jp/contests/abc122/tasks/abc122_a
A和T配对,C和G配对
输入一个字母,输出和他配对的字母

abc121_a White Cells

https://atcoder.jp/contests/abc121/tasks/abc121_a
H行W列的一个网格,其中h行w列被染色,问多少个格子没有被染色
注意没有被染色的格子数目,和选哪些行,哪些列染色无关

参考资料

C++字符串

C++字符串 和 C字符串 处理方式不一样,所以几乎所有问题都有两种解决方法

char 数组与 string 类型对比

•【2】字符数组与字符串的关系
•【2】字符数组的综合应用
•【2】string类定义、相关函数引用
•【3】string类的综合应用

存储

char 数组
string

读入

cin 读入 char 数组
cin 读入 string
scanf 读入 char 数组
scanf 不能读入 string

输出

cout
printf
puts

字符串长度

字符串比较

find

string s;
s.find("abc")
找到了,会返回第一次出现的下标(也就是'a'的下标)
没找到会返回 string::npos
这个数字实际上是 4294967295 (2**32 - 1)他是无符号的
用 s.find("") == -1 表示没找到
用 s.find("") == string::npos 表示没找到

string

#include <bits/stdc++.h>
using namespace std;
string s;
int main()
{
    cin >> s; // 读入一个不含空白的字符串,比如输入 XYZ
    cout << s.size() << endl; // s的长度
    cout << s.front() << endl; // s开头的字符
    cout << s[0] << endl; // s开头的字符,注意是s[0]
    cout << s[0] << " " << s[1] << " " << s[2] << endl;
    // 如果s的长度是3,那么3个字符分别是 s[0] s[1] s[2]。没有 s[3]
    // 这是一种特殊的数组,之后会详细讲
    cout << s.back() << endl; // s结尾的字符
    cout << s[s.size() - 1] << endl; // s结尾的字符
    return 0;
}

abc068_a ABCxxx

https://atcoder.jp/contests/abc068/tasks/abc068_a
输入一个三位数N,输出ABC和N

abc132_a Fifty-Fifty

https://atcoder.jp/contests/abc132/tasks/abc132_a
输入长度为4的字符串,问这个字符串是不是恰好包含两种字符,每种字符各两个

abc146_a Can't Wait for Holiday

https://atcoder.jp/contests/abc146/tasks/abc146_a
输入星期中的一天,问到下周日还有几天

abc166_a A?C

https://atcoder.jp/contests/abc166/tasks/abc166_a
输入ABC和ARC之一,输出另一个

abc175_a Rainy Season

https://atcoder.jp/contests/abc175/tasks/abc175_a
输入一个长度为3,包含R和S的字符串,问最多连续几个R?分8种情况讨论。

abc217_a Lexicographic Order

https://atcoder.jp/contests/abc217/tasks/abc217_a
输入两个字符串S和T,问S<T是否成立,输出Yes或No,字符串之间可以直接比较

abc218_a Weather Forecast

https://atcoder.jp/contests/abc218/tasks/abc218_a
输入n,输出一个长度为n的字符串,问第n个字符是不是o,注意字符串下标从0开始。

abc179_a Plural Form

https://atcoder.jp/contests/abc179/tasks/abc179_a
输入一个字符串,如果以s结尾,结尾加es,否则加s。

abc224_a Tires

https://atcoder.jp/contests/abc224/tasks/abc224_a
输入一个字符串,如果以er结尾,输出er,如果以ist结尾,输出ist。

可以用string也可以用int

abc073_a September 9

https://atcoder.jp/contests/abc073/tasks/abc073_a
输入一个二位数,问是否包含9

abc162_a Lucky 7

https://atcoder.jp/contests/abc162/tasks/abc162_a
输入一个三位数,问是否包含7

abc187_a Large Digits

https://atcoder.jp/contests/abc187/tasks/abc187_a
输入两个数字,问各个数位之和较大的和是多少。

lower_bound / upper_bound

unique

unique(起点, 终点)
和所有STL一样,左闭右开,(包括起点,不包括终点)
unique 只会去掉连续相同的数字
比如 对 1, 1, 2, 2, 1, 1, 2, 2 unique 结果是 1, 2, 1, 2
返回值是指向去重之后,最后一个元素之后的位置
所以常见用法是
m = unique(a, a + n) - a;
a[0], a[1], ..., a[n-1]去重
去重之后有 m 个不同的数字
并且存在 a[0], a[1], ..., a[m-1] 位置上

a[m], a[m+1], ..., a[n-1] 应该不会变
但是你的程序不要利用这个性质

unique 只会去掉连续相同的数字
如果想不连续相同的也去掉,一般先 sort,让相同的是连续一段

去重也可以用 set

https://atcoder.jp/contests/abc164/tasks/abc164_c
https://atcoder.jp/contests/abc240/tasks/abc240_b

#include <bits/stdc++.h>
using namespace std;
int a[] = {1, 1, 2, 2, 1, 1, 2, 2};
//         1  2  1  2  
//                     ^ return pointer
//  unique(...) - a =  4
int main()
{
	int n = 8;
	// sort(a, a + n);
	for (int i = 0; i < 8; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	n = unique(a, a + n) - a;
	for (int i = 0; i < 8; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	cout << n << endl;
	return 0;
}

next_permutation

next_permutation 可以生成下一个排列,结合 do while 循环可以生成全排列。

P1706 全排列问题

https://www.luogu.com.cn/problem/P1706

#include <bits/stdc++.h>
using namespace std;
int n, a[10];
int main()
{
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        a[i] = i + 1;
    }
    do
    {
        for (int i = 0; i < n; i++)
        {
            cout << "    " << a[i];
        }
        cout << endl;
    }
    while (next_permutation(a, a + n));
    return 0;
}

如果数组中有重复的数字会怎样?
只会生成所有不同的排列。

abc221_c Select Mul

https://atcoder.jp/contests/abc221/tasks/abc221_c
输入一个数字,把这个数字的各位分成两部分并重排,问重排之后的最大乘积是多少

abc150_c Count Order

https://atcoder.jp/contests/abc150/tasks/abc150_c
输入两个排列P和Q,假设是第a个和第b个排列,输出abs(a-b)

abc145_c Average Length

https://atcoder.jp/contests/abc145/tasks/abc145_c
输入n个点,想访问所有点有n!个顺序,每种顺序可以算一个总距离,问这n!个距离平均值是什么

abc215_c One More aab aba baa

https://atcoder.jp/contests/abc215/tasks/abc215_c
输入一个字符串,问全排列第k小的字典序是什么,输入的字符串可能有重复字母

CF124B Permutations

https://codeforces.com/problemset/problem/124/B
输入n个k位数字,你可以重排这n个数字各个位上的值,问这n个数字的最大值减最小值最大是多少

排序

排序的基本概念(稳定性等)
3种 n^2的排序
3种 n log n 的排序
结构体排序

3个 O(n2)O(n^2) 的排序

冒泡
选择
插入

O(n log n)
快排
归并
堆排
3个 O(nlogn)O(n \log n) 的排序

3个不基于比较的排序
计数排序
基数排序
桶排序(分段计数排序)

排序和离散化

主要讲解 sort, unique, lower_bound 三个函数的用法

CF670C Cinema

https://codeforces.com/problemset/problem/670/C
有n个人,第i个人会语言a[i],有m个电影,第j个电影的语音是b[j],字幕是c[j]
问选哪个电影,可以让最多的人听懂或看懂,输入电影下标,如果多个答案输入任意一个都可以

abc213_c Reorder Cards

https://atcoder.jp/contests/abc213/tasks/abc213_c
输入两个长度为n个序列a[i]和b[i],希望保持数组中相对大小关系不变,并把值域缩小。
输出缩小之后的结果

CF344A Magnets

https://codeforces.com/problemset/problem/344/A
输入n个字符串,去掉相邻相同的,问结果剩下几个字符串

abc143_c Slimes

https://atcoder.jp/contests/abc143/tasks/abc143_c
输入n个字符,去掉相邻相同的,问结果剩下几个字符

abc164_c gacha

https://atcoder.jp/contests/abc164_c/tasks/abc164_c
输入n个字符串,去掉相邻相同的,问结果剩下几个字符串

abc221_d Online games

https://atcoder.jp/contests/abc221/tasks/abc221_d
输入n个区间,输出n个数字,第k个数字表示被覆盖k次的长度

O(n^2)

for (int i = 0; i < n; i++)
{
	for (int j = 1; j < n; j++)
	{
		if (a[j - 1] > a[j])
		{
			swap(a[j - 1], a[j]);
		}
	}
}

交换的次数 = 逆序对的个数
a[i] > a[j] && i < j 的个数

sort
intro sort 是 快排 堆排 选择 排序合在一起的

任意2个元素必须可以比较,如果不可以比较当做等于
比较 全序关系
a < b && b < c 那么 a < c
a == b && b == c 那么也要a == c

P4604 [WC2017]挑战

如果排序的数字都是整数,并且范围很小
输入一个长度为n的数组
输入m个询问,每个询问是一个区间[L, R]
将所有区间,按照左端点排序
vector<pair<int, int> >b[100020];
for (int i = 1; i <= m; i++)
{
cin >> L >> R:
b[L].push_back(make_pair(i, R));
}

分段计数排序
计数相当于只能排1维

排 (x, y)
只能要求按x排序,如果x相同,那么保持原序
如果想先比较x,如果x相同,y小的在前
那么就先按y排序,再按x排

sort
重载运算符
自定义比较函数
仿函数

输入n个数字,问有多少个不同数字/输入每个数字的时候,回答之前是否出现过
set s;
for (int i = 1; i <= n; i++)
{
cin >> x;
s.insert(x);
}
问题:set会越来越大,导致越来越慢

int mod = rand();
set s[10007];
for (int i = 1; i <= n; i++)
{
cin >> x;
s[x % 10007].insert(x);
}
在随机情况下每个set都不会太大,效率大大提高。
Hash(x) = (x % 10007) Hash函数

  1. 相同的数字Hash之后必须相同
  2. 不同的数字Hash之后尽量不同
  3. 计算的要快
  4. 范围不能太大

大部分写法中,并不会使用set,而会使用vector,链表
%10007之后一定会有相同的,怎么处理

sha1 2**256

  1. 不可逆
  2. 唯一方法一个一个试

问题:找一个字符串以今天日期为开始,Hash的最后10位是0
如果认为Hash是随机的话,那么如果一个人找到了Hash最后10位是0的字符串
可以认为他计算了1024次

P1059 明明的随机数
排序去重

  1. 用set,去重
  2. 注意到a[i]范围很小,统计每个数字是否出现过
  3. sort unique
    sort(a, a + n);
    n = unique(a, a + n) - a;

unique 是不排序的

P1296 奶牛的耳语
二分/双指针法

  1. 二分
    统计每个数字p[i]前面有几个p[j]使得p[i]-p[j] <= d
    统计每个数字p[i]后面有几个p[j]使得p[j]-p[i] <= d

  2. 双指针法

P1138 第k小整数
排序

P1097 统计数字

  1. map
  2. 排序

P1051 谁拿了最多奖学金
模拟

P1080 国王游戏
按乘积排序

P1068 分数线划定
模拟

P1102 A-B 数对

  1. map
  2. 排序

P3913 车的攻击
行去重
列去重
sort 比 set 快很多

只是为了去重,而不需要有序的话,Hash也是很好的选择。

P4305 [JLOI2011]不重复数字
set暴力,
unordered_set
unordered_map
没有有序的性质了(不能lower_bound upper_bound)
不需要指定
出题人能不能造一个数据,让unordered_set超时?

P1469 找筷子
xor
(a xor a) == 0
(a xor b) == (b xor a)
(a xor b) xor c = a xor (b xor c)
如果不卡空间 map暴力 / 排序

P1866 编号
排序,算数

P1007 独木桥
P5835 [USACO19DEC]Meetings S
相遇调头

P1094 纪念品分组
双指针法

P1093 奖学金
模拟排序

P1626 象棋比赛
模拟排序

P4829 kry loves 2048
用堆/队列贪心,类似合并果子

P3918 [国家集训队]特技飞行
排序,最大的放两边

P2969 [USACO09DEC]Music Notes S
排序二分
1 1 2 3 3 3

  1. 前缀和,二分
    2 1 3

2 3 6
2. 排序所有询问,扫一遍

while (还有询问)
while (这一段在询问之前)
过掉
回答这个询问

P1271 【深基9.例1】选举学生会
计数排序

vector b[1000];
for (int i = 0; i < m; i++)
{
cin >> x >> name;
b[x].push_back(name);
}

P4378 [USACO18OPEN]Out of Sorts S
pair 带下标排序,排序

P4375 [USACO18OPEN]Out of Sorts G
pair 带下标排序,排序

vector / queue / stack / deque

这些数据结构都是简单的数组,即使不用STL,也可以很方便的实现

set 集合

set 集合 有序的
multiset 多重集合 有序的

支持的操作:

不支持的操作

询问第k大值
询问一个值是第几大

priority_queue 能做的,set 都能做,但是 set 慢一些

C++ set = Java TreeSet (sorted set)
C++ unordered_set = Java HashSet
Python 用 SortedList 实现 C++ set 的功能

枚举

for (auto i: s)
{

}

加入

可以利用 insert 的返回值判断是否删除成功

删除

multiset 删除一个 要先 find 再 erase

map

map 映射

同时存储一对 key 和 value
可以根据 key 找到 value

支持操作:
加入任意值
删除任意值
询问 lower_bound 和 upper_bound
询问最大值最小值

不支持:
询问第k大值
询问一个值是第几大

Java

C++ map = Java TreeMap (sorted map)
C++ unordered_map = Java HashMap

参考题目

abc253_c

https://atcoder.jp/contests/abc253/tasks/abc253_c

C++ STL

简介

vector / queue / stack / deque

这些数据结构都是简单的数组,即使不用STL,也可以很方便的实现

priority_queue

priority_queue 优先队列/堆

加入任意值
询问最大值
删除最大值

优先队列是唯一一个默认大的在前的内置函数
priority_queue is the only exception, which largest is the first

其他的比如
set
map
sort
lower_bound
都是小的在前
the first is the smallest

每个位置都 -= M 可以删去一段,和 < 0 说明 ans < M R = M
每个位置都 -= M 删去任意一段,和 >= 0 说明 ans >= M L = M

include

std::priority_queue

3个操作
加入一个数字 push
取出最大值 top
删去最大值 pop
(不能删去任意值)
如何支持删除

  1. 直接用set
  2. 每次取top之后,检查一下是不是最新版本
  3. 用一个堆,记录删去了哪些元素,如果2个堆的堆顶相同,同时删去。

可以删除 相当于可以修改

priority_queue q;

priority_queue
可以是结构体
结构体需要重载 <
或者传比较函数

priority_queue 默认 大的数字在前 top 是最大的
这个和 其他STL都不相同

map set sort lower_bound ...
都是小的在前

priority_queue 支持的操作和 set/multiset 很相似
priority_queue 快一点,但是不支持删除任意元素
set/multiset 可以删除任意元素,set支持lower_bound,前驱,后继

lower_bound 查询set中 >=x 最小元素是什么?
upper_bound 查询set中 >x 最小元素是什么?

std::priority_queue<int, std::vector<int>, std::less<int> > q2;
等价于
std::priority_queue<int> q2;
大 的在前

std::priority_queue<int, std::vector<int>, std::greater<int> > q2;
小 的在前

堆是如何实现的
https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3378.cpp
如何用数组存储二叉树
根节点是0,x的左孩子是2x+1,右孩子是2x+2,父亲节点是(x-1)/2
根节点是1,x的左孩子是2x,右孩子是2x+1,父亲节点是x/2

堆的性质(以大根堆为例)
每个节点>=自己的两个孩子
最大值一定在根节点(堆顶)
https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3378.cpp

调整操作
up 向上调整
每次和自己的父亲节点比

down 向下调整
和自己较大的孩子比
(自己可能0个孩子,1个孩子,2个孩子)

  1. 取最大值
    直接返回堆顶

  2. 如何加入一个数字
    加在末尾,向上调整

  3. 删除
    堆顶和最后一个交换,堆顶向下调整

  4. 初始化
    输入一个数组,如何初始化成堆的样子。

显而易见的做法,一个一个加入
for (int i = 1; i <= n; i++)
{
up(i);
}
时间复杂度O(n log n)

另一个做法
for (int i = n; i >= 1; i--)
{
down(i);
}
O(n)

对于自己实现的堆,可以支持删除任意值
需要记录每个值的位置(所以在所有swap的时候,需要同时交换位置)

swap(a[需要删除的位置], a[n]);
n--;
up(需要删除的位置)
down(需要删除的位置)

P1090 合并果子
Huffman Code

k叉?(每次合并<=k堆)
合并一次减少k-1堆
如果(n-1)%(k-1)==0 和之前一样,贪心即可。
如果(n-1)%(k-1)>0 第一次合并最小的(n-1)%(k-1)+1堆,其他和之前一样。

P1334 瑞瑞的木板
和 合并果子 一样
需要long long

STL函数
make_heap
pop_heap
push_heap

P6033 合并果子 加强版
合并果子的 计数排序 队列优化

P1177 【模板】堆排序

快速排序
时间 O(n log n)
(额外)空间 O(log n)
求第k大数 O(n)
nth_element

堆排序 O(n log n)
(额外)空间 O(1)
最坏时间复杂度O(n log n)

O(n)建堆
pop n次

归并排序 O(n log n)
(额外)空间 O(n)
求逆序对 O(n log n)
可以用硬盘,不一定都要在内存
可以从2个文件中读入,输出到第3个文件
比如你需要排序非常非常大的文件,10G

实际题目需要排序的话,一定是
sort 函数
introsort
效率非常高
主要掌握比较函数如何实现

P1168 中位数

#include <bits/stdc++.h>
using namespace std;
int n, x;
priority_queue<int, vector<int>, greater<int> > q1; 小根堆
priority_queue<int, vector<int>, less<int> > q2; 大根堆
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> x;
		if (i == 1 || x > q1.top()) {
			q1.push(x);
		} else {
			q2.push(x);
		}
		if (q1.size() < q2.size()) {
			q1.push(q2.top());
			q2.pop();
		}
		if (q1.size() > q2.size() + 1) {
			q2.push(q1.top());
			q1.pop();
		}
		// 时刻保证 q1.size() == q2.size() || q1.size() == q2.size() + 1
		// 时刻保证 q1.top() >= q2.top();
		if (i % 2 == 1) {
			printf("%d\n", q1.top());
		}
	}
	return 0;
}

P1801 黑匣子
和中位数类似的思路,2个堆

P1631 序列合并

注意到a[i] + b[j] 如果ij>n 一定不会被选择
考虑有多少个位置满足 i
j <= n
n / 1 + n / 2 + .. + n / n 约等于 n log n
在n log n个数字中求最小的n个

for (int i = 1; i <= n && i <= a.size(); i++)
{
	for (int j = 1; j * i <= n && j <= b.size(); j++)
	{
		re.push_back(a[i-1] + b[j-1]);
	}
}
sort(re.begin(), re.end());
re.resize(n);
return re;

P2085 最小函数值
Fi(x) < Fi(x+1)
一定先取x再取x+1

P3620 [APIO/CTSC 2007]数据备份
2 1 2 6

1000000000 1 2 3 4

a b c 选择中间的b
变成1个数字
a+c-b

1380 夹克老爷的逢三抽一

P1878 舞蹈课

P5120 [USACO18DEC]Convention II S 用堆贪心

P1382 楼房
扫描线 set

P2707 Facer帮父亲
P2085 最小函数值

P3045 [USACO12FEB]Cow Coupons G

优先队列 可以优化 dijkstra

Java PriorityQueue

P2107
带反悔的贪心
一定是从原点向右走到某个位置,在路过的任务中选最小的若干个来做。

P2949
带反悔的贪心

假设决定了做哪些任务,一定是按照截止时间排序来做。
对于任意一个时间x,
截止时间<=x的任务 消耗的总时间 必须<=x
截止时间<=x的任务 个数 必须<=x

扫描所有任务,每次直接选择,如果当前选择的任务个数 > 截止时间,在所有已经选择的任务中,
扔掉获利最小的(扔掉获利最小的任务时,不考虑截止时间)

假设每个任务消耗的时间不相同
可以用背包解决
先排序
每个任务更新时,只更新到自己的截止时间

背包时,不同物品的放置顺序是有关系的
先考虑假设要选择一个集合
按哪种顺序一定可以放置成功
先把这些物品按照这个顺序排序

https://codeforces.com/gym/101623/attachments
https://codeforces.com/gym/101623/problem/I

set 集合

set 集合 有序的
multiset 多重集合 有序的

支持的操作:

不支持的操作

询问第k大值
询问一个值是第几大

priority_queue 能做的,set 都能做,但是 set 慢一些

C++ set = Java TreeSet (sorted set)
C++ unordered_set = Java HashSet
Python 用 SortedList 实现 C++ set 的功能

枚举

for (auto i: s)
{

}

加入

可以利用 insert 的返回值判断是否删除成功

删除

multiset 删除一个 要先 find 再 erase

map

map 映射

同时存储一对 key 和 value
可以根据 key 找到 value

支持操作:
加入任意值
删除任意值
询问 lower_bound 和 upper_bound
询问最大值最小值

不支持:
询问第k大值
询问一个值是第几大

Java

C++ map = Java TreeMap (sorted map)
C++ unordered_map = Java HashMap

参考题目

abc253_c

https://atcoder.jp/contests/abc253/tasks/abc253_c

题目

P1503 鬼子进村
P2073 送花
P2234 [HNOI2002]营业额统计
P4404 [JSOI2010]缓存交换
P2061 [USACO07OPEN]City Horizon S
P2161 [SHOI2009]会场预约
P1102 A-B 数对
P5250 【深基17.例5】木材仓库
P5266 【深基17.例6】学籍管理
P4305 [JLOI2011]不重复数字

P1059 明明的随机数
P2141 珠心算测验
P1125 笨小猴
P2814 家谱
P3370 【模板】字符串哈希
P3405 [USACO16DEC]Cities and States S
P5250 【深基17.例5】木材仓库
P5266 【深基17.例6】学籍管理
P1918 保龄球
P4305 [JLOI2011]不重复数字
P3879 [TJOI2010]阅读理解
P1059 明明的随机数
P1503 鬼子进村
P2073 送花
P2141 珠心算测验

sort 结构体排序
pair<int, int>

https://codeforces.com/
https://atcoder.jp/
https://www.luogu.com.cn/

https://en.cppreference.com/w/
http://www.cplusplus.com/reference/

STL Algorithm
sort

STL Container

Java

HashSet
TreeSet
TreeMap
ArrayList

  1. insert an element
  2. query lower_bound / upper_bound

TreeSet s;
for (int i: s)
{
if (i % 2 == 1)
{
s.erase(i);
}
}

multiset in Java

  1. add a new class member index
  2. use tree map to store the number of occurence.

STL

next_permutation
2 1 4 3 -> 2 3 1 4

inplace_merge

Contestants using C++ may find the following code from KACTL helpful. Known as the Barrett reduction, it allows you to compute a%b several times faster than usual, where b>1

is constant but not known at compile time. (we are not aware of such an optimization for Java, unfortunately).

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

typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
	ull b, m;
	FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
	ull reduce(ull a) {
		ull q = (ull)((L(m) * a) >> 64);
		ull r = a - q * b; // can be proven that 0 <= r < 2*b
		return r >= b ? r - b : r;
	}
};
FastMod F(2);

int main() {
	int M = 1000000007; F = FastMod(M);
	ull x = 10ULL*M+3; 
	cout << x << " " << F.reduce(x) << "\n"; // 10000000073 3
}

pair<int, int>

vector

for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
	cout << *it << endl;
}

for (int i: s)
{
	cout << i << endl;
}

P2061 [USACO07OPEN]City Horizon S
(pos, height)

P4404 [JSOI2010]缓存交换
+1
+2
+3 -2

+2 -1

1 2 3 1 2 3
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
4 5 6
.....

需要一个有序集合,需要删除 最近一次用最晚的元素

集合存储 (下一次使用的时间,元素是什么)
可以很方面的找到 下一次使用的时间 最大的是什么
把一个元素加入集合的时候,如何知道下一次使用的时间

下标 0 1 2 3 4 5
数值 1 2 3 1 2 3
下次 3 4 5 6 6 6

下标 0 1 2 3 4 5
数值 1 1 1 1 1 1
下次 1 2 3 4 5 6

{}
{1}

for

P1918 保龄球

sort binary_seart
sort lower_bound

map

map<int, int>

wrong:
s.erase(it);
it++

right:
s.erase(it++)

increase it get the new value
erase it
assign the new value to it

P2073 送花

P1059 明明的随机数

  1. set / TreeSet
  2. sort unique
  3. array

100 300 200 400 -> 1 3 2 4
sort unique lower_bound

P2161 [SHOI2009]会场预约

https://www.luogu.com.cn/blog/Nartsam/solution-p2161
重载运算符时可以发生
a < b
b < c
a == c
的情况

用 s.find() 直接找冲突区间

P2234 [HNOI2002]营业额统计

P3374 【模板】树状数组 1

brute force
change O(1)
query O(n)

prefix sum
change O(n)
query O(1)

blocking algorithm
change O(2)
query O(sqrt(n))

3-layer blocking algorithm
change O(3)
query O(n^(1/3))

segment tree / binary index tree
change O(log n)
query O(log n)

https://github.com/wwwwodddd/Zukunft/tree/master/luogu

https://github.com/bqi343/USACO

C++
比较快
功能比较多

https://www.luogu.com.cn/problem/P1059

  1. 计数

sort(a, a + n);
int n = unique(a, a + n) - a;

1 1 2 2 1 1 2 2
1 2 1 2

100 200 300 400
1 2 3 4

结构体
排序
函数返回多个值

sort
set / Java TreeSet
unordered_set
map

https://www.luogu.com.cn/
中文,使用方便

http://codeforces.com/
英文,使用方便

http://atcoder.jp/
英文,题目比较偏

删除最大值 必须是
s.erase(--s.end());

s.erase(s.rbegin());

// char a, b;
short a, b;
(a - b)

s.find(x)
如果 x 在 set 中,返回 iterator
如果不在 返回 s.end()

s.lower_bound(x) set 中 >= x 最小的
s.upper_bound(x) set 中 > x 最小的
如果存在,返回 iterator
如果不存在,那么返回 s.end()

如果 x 不在 s 中,那么 lower_bound 和 upper_bound 结果相同(不存在 == x 的位置)

--s.lower_bound(x) set 中 < x 最大的
--s.upper_bound(x) set 中 <= x 最大的
需要注意如果x已经是最小的数字,那么可能出错。

#include <bits/stdc++.h>
using namespace std;
int main()
{
	set<int> s;
	for (int i = 0; i < 1000000; i++)
	{
		s.insert(i);
	}
	long long c = 0;
	for (int i = 0; i < 1000000; i++)
	{
		c += *s.lower_bound(i);
		c += *lower_bound(s.begin(), s.end(), i);
	}
	cout << c << endl;
	return 0;
}

char o[2]
scanf("%s", o);
if (o[0] == 'D')

char o;
scanf(" %c", &c);
if (o == 'D')

char o;
cin >> o;
if (o == 'D')

s.erase(it);
it++;

s.erase(it++);

s.erase(it)
it = it+1;

https://www.luogu.com.cn/problem/P4404

首先用 map 对于每个 i 求出 p[i]
p[i] 表示下一次出现 a[i] 是什么位置
p[i] > i && a[p[i]] == a[i] && p[i] 最小
如果 i位置 之后再也没出现过 a[i]
那么 p[i] = n

for (pair<int, int> i: g)
{
i.first;
i.second;
}

需要一个set 维护
(下次使用的时间, 这是什么内存)

vector 变长数组
图论

初始16个位置
每次用完都翻倍
16 -> 32 -> 64 -> 128
排序/分组
输入一些名字和年龄,将所有人按年龄排序

和 链表

sort unique lower_bound upper_bound

离散化
5
400 200 200 300 100
4 2 2 3 1

100 200 300 400
^

unique 之前 1 1 0 0 1 1 0 0
unique 之后 1 0 1 0
下标 0 1 2 3 4 返回值=指向4的指针
unique 之前 1 1 1 2 2 2 3 3
unique 之后 1 2 3
下标 0 1 2 3 返回值=指向3的指针

upper_bound(aa, aa + ac, x) - lower_bound(aa, aa + ac, x)
问数组 aa, aa + ac 中,x出现的次数

lower_bound(a.begin(), a.end(), x)  vector a 中 >= x 最小的位置
upper_bound(a.begin(), a.end(), x)  vector a 中 >  x 最小的位置

对于整数来说 > x 等价于 >= x + 1
upper_bound(a.begin(), a.end(), x) == lower_bound(a.begin(), a.end(), x + 1)

1 1 1 2 2 2 3 3
            ^ upper_bound 寻找 2
      ^ lower_bound 寻找 2
      upper_bound - lower_bound == 3 就是 2 的出现次数

详细的自己查 C++ Reference

priority_queue 有 top
queue 只有 front

set 是 begin 和 end
set 有 iterator

priority queue
queue
stack
没有 iterator

广度优先搜索 BFS

算法特点

每个状态比较小,求最小距离
队列

深度优先搜索 DFS
状态总数有限,每个状态比较大
递归(可以自己用非递归和栈实现)

有多少个连通块?
每个连通块多大?

struct Node
{
int first, second;
}a, b;

a < b;
max(a, b);

P1443

https://www.luogu.org/problemnew/show/P1443

P1747

https://www.luogu.org/problemnew/show/P1747
马象

P1746

https://www.luogu.org/problemnew/show/P1746
字符地图上,有障碍物

P2298

https://www.luogu.org/problemnew/show/P2298
单起点,多终点

P1332

https://www.luogu.org/problemnew/show/P1332
多起点,多终点

BFS are always related with shortest distance
If the length of edges are all 1.
Even we can use queue to implement priority_queue

P1825 [USACO11OPEN]Corn Maze S

https://www.luogu.com.cn/problem/P1825
xor
(a ^ a) == 0
(a ^ b) == (b ^ a)
(a ^ b) ^ c == a ^ (b ^ c)

a ^ b ^ a = b

(x1, y1) (x2, y2)

given x, y, we know (x, y) is (x1, y1) or (x2, y2)
we want to get another

(x1 ^ x2 ^ x, y1 ^ y2 ^ y)
(x1 + x2 - x, y1 + y2 - y)

#include <bits/stdc++.h>
using namespace std;
char s[302][302];
int d[302][302];
int dx[] = {0, 0, -1, 1};
int dy[] = {-1, 1, 0, 0};
int tx[256];
int ty[256];
int n, m;
bool in(int x, int y) {
    return 0 <= x && x < n && 0 <= y && y < m;
}
int bfs(int x, int y) {
    queue<int> q;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            d[i][j] = -1;
        }
    }
    d[x][y] = 0;
    q.push(x);
    q.push(y);
    while (q.size()) {
        x = q.front();
        q.pop();
        y = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            char ch = s[nx][ny];
            if (in(nx, ny) && ch != '#') {
                if (isalpha(ch)) {
                    nx ^= tx[ch];
                    ny ^= ty[ch];
                }
                if (d[nx][ny] == -1) {
                    d[nx][ny] = d[x][y] + 1;
                    if (s[nx][ny] == '=') {
                        return d[nx][ny];
                    }
                    q.push(nx);
                    q.push(ny);
                }
            }
        }
    }
    return -1;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%s", s[i]);
    }
    int sx = -1;
    int sy = -1;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (s[i][j] == '@') {
                s[i][j] = '.';
                sx = i;
                sy = j;
            }
            if (isalpha(s[i][j])) {
                tx[s[i][j]] ^= i;
                ty[s[i][j]] ^= j;
            }
        }
    }
    printf("%d\n", bfs(sx, sy));
    return 0;
}

memset

逐字节赋值
对于 int 类型
0x 01 01 01 01 : 16843009
0x 00 00 00 01 : 1
0x 00 00 00 00 : 0
0x ff ff ff ff : -1
0x 3f 3f 3f 3f : 1061109567
0x 7f 7f 7f 7f : 2139062143
0x c0 c0 c0 c0 : -1061109568
0x 80 80 80 80 : -2139062144

memset(a, 1, sizeof a);
a[0] == 0x01010101 == 16843009

差分前缀和

定义

对于长度为nn的数列a1,a2,,ana_1, a_2, \ldots, a_n

定义他的前缀和为s0=0,si=si1+ai=j=1iais_0 = 0, s_i = s_{i-1} + a_i = \sum_{j=1}^i a_i

如果需要获得aia_i数组的一部分的和,可以直接通过前缀和相减得到
srsl1=i=lrai=al+al+1++ars_r - s_{l-1} = \sum_{i=l}^r a_i = a_l + a_{l + 1} + \cdots + a_r
定义他的差分为bi=aiai1b_i = a_i - a_{i - 1}
如果需要修改aia_i一个区间的值,那么可以只修改blb_{l}br+1b_{r+1}

高维情况

对于二维情况和高维情况可以类似定义前缀和
sn,m=i=1nj=1mai,js_{n, m} = \sum_{i = 1}^n \sum_{j = 1}^m a_{i, j}

可以用容斥的方法

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
    }
}

也可以先每一行求前缀和,再每一列求前缀和。

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        a[i][j] += a[i][j - 1];
    }
}
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        a[i][j] += a[i - 1][j];
    }
}

参考题目

Luogu P1314
二分WW,然后计算前缀中满足wjWw_j \geq W的个数和vjv_j之和。

Luogu P1969
Luogu P5019
差分,求出所有正数之和(同时也是负数之和的相反数),即为答案。

Luogu P4552
差分,求出所有正数之和(同时也是负数之和的相反数),即为第一问答案。
第二问答案为第一个数字和最后一个数字之差:abs(a[1] - a[n])

Luogu P2213
(i,j)(i, j)映射到(i+j,ij+n)(i + j, i - j + n),然后每次询问是一个矩形区域,二维部分和。

参考资料

https://en.wikipedia.org/wiki/Prefix_sum

前缀和

CF816B Karen and Coffee

https://codeforces.com/contest/816/problem/B

a[1] a[2] a[3] a[4]

b[i] = b[i-1] + a[i]

b[0] = 0
b[1] = a[1]
b[2] = a[1] + a[2]
b[3] = a[1] + a[2] + a[3]
b[4] = a[1] + a[2] + a[3] + a[4]
b[5] = a[1] + a[2] + a[3] + a[4] + a[5]
b[i] = a[1] + a[2] + ... + a[i]

如果a数组,输入之后不再改变
求前缀和

a[l] + a[l+1] + ... + a[r]的和 = b[r] - b[l - 1]

差分
b[i] = a[i] - a[i-1]

希望b[l], b[l+1], ..., b[r]增加 += 1
那么只需要改 a 数组中的两项
a[l] += 1
a[r + 1] -= 1

https://www.luogu.com.cn/problem/P1865
前缀和,质数个数

树状数组

f[i] 以a[i]结尾的最大的一段的和

f[i] = a[i] + max(0, f[i-1])

用前缀和解决最大子段和
b[i] - min(b[0], b[1], b[2], ..., b[i-1])

1到n的m次方和 mod 10007
mod的数字很小,有循环节
(1**m + ... + 10007 m) % 10007 暴力
n % 10007的答案 + n / 10007 * 循环节
(1
m + ... + 10007 **m) % 10007 在绝大多数情况下是0
例外 m = 10006的时候

有若干个操作
每个操作(s, t, d)

a[s] += d
a[s+1] += d
...
a[t] += d

区间 += d

最后询问每个位置的值(和初始,能提供的教室个数比较)

区间 += d
等价于在差分数组中,修改2个位置。
b[i] = a[i] - a[i-1]
b[s] += d;
b[t + 1] -= d;
非常适合,先修改,最后一起询问
(先修改差分数组,求前缀和,最后一起回答所有答案)

P1083 借教室

借教室,微小的优化

如果前mid个可以
left = mid
如果前mid个不可以
right = mid
前mid个可以之后
直接将初始数组 -= 前mid个操作需要的数量
之后判断只需要从从mid+1开始做就可以了。

时刻保持
calc(L) <= s
calc(R) > s

L == R-1

P1314 聪明的质监员

直接二分权值,范围1到1000000
将所有权值排序,再二分

找到
s[l] == s[r]

r - l最大值是多少
对于每个值x,计算出s[l]==x最小的l是多少。

P1114 “非常男女”计划

#include <bits/stdc++.h>
using namespace std;
int l[200010],ans,n;
int main()
{
    cin>>n;
    int t = n;
    memset(l, -1, sizeof l);
    l[0] = 0;
    for (int i=1;i<=n;i++){
        int x; cin>>x;
        if (x == 0)
        {
        	t++;
        }
        else
        {
        	t--;
        }
        if (l[t] == -1)
        {
        	l[t] = i;
        }
        ans = max(ans, i - l[t]);
    }
    cout<<ans<<endl;
    return 0;
}

P3131 [USACO16JAN]Subsequences Summing to Sevens

(s[r] - s[l]) % 7 = (a[l + 1] + a[l + 2] + .. + a[r]) % 7 == 0

P1865 A % B Problem

欧拉筛法 O(n)
埃拉托斯特尼筛法 O(n log log n) = O(n * (1/2 + 1/3 + 1/5 + 1/7 + ...))

P1360 [USACO07MAR]Gold Balanced Lineup G

排序,或者 map<vector, int>

二维和高维前缀和

a[1 .. n][1 .. m]
询问
b[i][j] = sum(a[1 .. i][1 .. j])

for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
b[i][j] = b[i-1][j] + b[i][j-1] - b[i-1][j-1] + a[i][j]
}
}
等价于
每一行求前缀和

for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		b[i][j] = b[i][j - 1] + a[i][j];
		// a[i][j] += a[i][j - 1];
	}
}

每一列求前缀和

for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		b[i][j] += b[i - 1][j];
		// a[i][j] += a[i - 1][j];
	}
}

3维情况

a[1 .. n][1 .. m][1 .. l]

for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		for (int k = 1; k <= l; k++)
		{
			b[i][j][k] = sum(a[1 .. i][1 .. j][1 .. k])
			b[i][j][k] = b[i][j][k-1] + b[i][j-1][k] + b[i-1][j][k]
			           - b[i][j-1][k-1] - b[i-1][j][k-1] - b[i-1][j-1][k]
			           + b[i-1][j-1][k-1] + a[i][j][k]
		}
	}
}

非常麻烦

for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		for (int k = 1; k <= l; k++)
		{
			a[i][j][k] += a[i][j][k - 1];
		}
	}
}
for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		for (int k = 1; k <= l; k++)
		{
			a[i][j][k] += a[i][j - 1][k];
		}
	}
}
for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= m; j++)
	{
		for (int k = 1; k <= l; k++)
		{
			a[i][j][k] += a[i - 1][j][k];
		}
	}
}

分别对每一维求前缀和

P1387 最大正方形

if (a[i][j]==1) f[i][j]=min(min(f[i][j-1],f[i-1][j]),f[i-1][j-1])+1;

P2671 求和

P3406 海底高铁

P3662 [USACO17FEB]Why Did the Cow Cross the Road II S

如果没有n的限制怎么做呢?

P4825 [USACO15FEB]Cow Hopscotch S

O(R*C*C)的做法

修改单点,询问区间
修改区间,询问单点

一般认为前者简单

P3173 [HAOI2009]巧克力

贪心,按照花费从大到小排序

交换相邻2个不会更优,所以排序就是最优解

P2879 [USACO07JAN]Tallest Cow S

区间 减一

P3909 异或之积

s0 = 0次方之和(等价于 个数
s1 = 1次方之和
s2 = 2次方之和
s3 = 3次方之和

ans = s1 * s1 * s1 - 3 * s2 * s1 + 2 * s3

P1714 切蛋糕

P2629 好消息,坏消息

单调队列

P4677 山区建小学

f[i][j] 前i个村庄,建j个学校最小代价

快速求出一个区间的最小代价

P2280 [HNOI2003]激光炸弹

P3819 松江1843路

带权值的中位数

P4552 [Poetize6] IncDec Sequence

spoj ADARAINB

https://www.spoj.com/problems/ADARAINB/

高精度

4.数值处理算法
•【4】高精度的加法
•【4】高精度的减法
•【4】高精度的乘法
•【4】求高精度整数除以单精度整数的商和余数

高精度算法普遍日渐落后于时代
Python 和 Java 都支持无限精度整数
高精度算法的考查几乎只能在中国看到

简介

int, long long类型都是有范围的,万一需要处理更大范围的数字怎么办呢?
只能手动模拟,或者使用其他语言。

一般的写法是从先存低位,再存高位,比如a[0]存个位,a[1]存十位……
a[i]对应的系数是10i10^i,计算起来比较方便。

整个数字有多少位,可以不存,即认为处理的数字高位有00
也可以存下来,并且在运算中维护,优化常数。

读入

先把整个数字读入,然后翻转,每个位置减去'0'
这样的写法可以方便的进行压位操作。

加减

直接模拟即可,最后扫一遍,处理进位和借位。
建议把运算和处理进位借位用两个循环实现。

乘法

可以用FFT优化。但是优化很小,因为高精度乘法可以压位优化,但是FFT后就不能压位了。
直接模拟即可,最后扫一遍,处理进位。

除法取模

高精度对一个范围比较小的数字的取模是一个比较简单的问题
如果是高精度除以高精度,一般来说二分比较好写。

进制转换

将一个ll位的高精度的数字从AA进制转化为BB进制。
如果存在整数nnmm使得An=BmA^n = B^m那么可以将AA进制的数字,每nn位一组,一起转化为mm位的BB进制,时间复杂度是O(l)O(l)的。
如果不满足以上条件,那么就只能每次模BB除以BB依次获得BB进制下的每一位数字了。
一些高精度语言,比如Ruby用了更快的算法(分治)

压位

进制的范围,只要不太大(比如不超过int类型)计算的复杂度是差不多的。
所以相对于十进制来说,更加常用的是万进制,甚至是10910^9进制,这样可以加快计算。

其他语言

因为Java, Python等语言均支持高精度,高精度在在线算法比赛出现的概率并不高。
当然也有一些比谁手写高精度快的题目,比如算高精度Pi。

参考题目

Luogu P1601
高精度加法

Luogu P2142
高精度减法

Luogu P1303
高精度乘法

Luogu P1480
高精度除以单精度

Luogu P1255
高精度加法算Fibonacci数,n=0n=0的情况非常智障,答案是00而不是11

Luogu P1080
国王游戏。排序然后计算答案。

Luogu P1727
算Pi,用
https://en.wikipedia.org/wiki/Machin-like_formula
的公式

Luogu P1729
算E,用定义就可以了。

Luogu P1096
输入nn,输出2(2n1)2(2^n - 1),可以利用精确输出double的特性。

Luogu P1760
输入nn,输出2n12^n - 1,可以利用精确输出double的特性。

加减乘
高精除模单精度

排序

排序的基本概念(稳定性等)
3种 n^2的排序
3种 n log n 的排序
结构体排序

3个 O(n2)O(n^2) 的排序

冒泡
选择
插入

O(n log n)
快排
归并
堆排
3个 O(nlogn)O(n \log n) 的排序

3个不基于比较的排序
计数排序
基数排序
桶排序(分段计数排序)

排序和离散化

主要讲解 sort, unique, lower_bound 三个函数的用法

CF670C Cinema

https://codeforces.com/problemset/problem/670/C
有n个人,第i个人会语言a[i],有m个电影,第j个电影的语音是b[j],字幕是c[j]
问选哪个电影,可以让最多的人听懂或看懂,输入电影下标,如果多个答案输入任意一个都可以

abc213_c Reorder Cards

https://atcoder.jp/contests/abc213/tasks/abc213_c
输入两个长度为n个序列a[i]和b[i],希望保持数组中相对大小关系不变,并把值域缩小。
输出缩小之后的结果

CF344A Magnets

https://codeforces.com/problemset/problem/344/A
输入n个字符串,去掉相邻相同的,问结果剩下几个字符串

abc143_c Slimes

https://atcoder.jp/contests/abc143/tasks/abc143_c
输入n个字符,去掉相邻相同的,问结果剩下几个字符

abc164_c gacha

https://atcoder.jp/contests/abc164_c/tasks/abc164_c
输入n个字符串,去掉相邻相同的,问结果剩下几个字符串

abc221_d Online games

https://atcoder.jp/contests/abc221/tasks/abc221_d
输入n个区间,输出n个数字,第k个数字表示被覆盖k次的长度

O(n^2)

for (int i = 0; i < n; i++)
{
	for (int j = 1; j < n; j++)
	{
		if (a[j - 1] > a[j])
		{
			swap(a[j - 1], a[j]);
		}
	}
}

交换的次数 = 逆序对的个数
a[i] > a[j] && i < j 的个数

sort
intro sort 是 快排 堆排 选择 排序合在一起的

任意2个元素必须可以比较,如果不可以比较当做等于
比较 全序关系
a < b && b < c 那么 a < c
a == b && b == c 那么也要a == c

P4604 [WC2017]挑战

如果排序的数字都是整数,并且范围很小
输入一个长度为n的数组
输入m个询问,每个询问是一个区间[L, R]
将所有区间,按照左端点排序
vector<pair<int, int> >b[100020];
for (int i = 1; i <= m; i++)
{
cin >> L >> R:
b[L].push_back(make_pair(i, R));
}

分段计数排序
计数相当于只能排1维

排 (x, y)
只能要求按x排序,如果x相同,那么保持原序
如果想先比较x,如果x相同,y小的在前
那么就先按y排序,再按x排

sort
重载运算符
自定义比较函数
仿函数

输入n个数字,问有多少个不同数字/输入每个数字的时候,回答之前是否出现过
set s;
for (int i = 1; i <= n; i++)
{
cin >> x;
s.insert(x);
}
问题:set会越来越大,导致越来越慢

int mod = rand();
set s[10007];
for (int i = 1; i <= n; i++)
{
cin >> x;
s[x % 10007].insert(x);
}
在随机情况下每个set都不会太大,效率大大提高。
Hash(x) = (x % 10007) Hash函数

  1. 相同的数字Hash之后必须相同
  2. 不同的数字Hash之后尽量不同
  3. 计算的要快
  4. 范围不能太大

大部分写法中,并不会使用set,而会使用vector,链表
%10007之后一定会有相同的,怎么处理

sha1 2**256

  1. 不可逆
  2. 唯一方法一个一个试

问题:找一个字符串以今天日期为开始,Hash的最后10位是0
如果认为Hash是随机的话,那么如果一个人找到了Hash最后10位是0的字符串
可以认为他计算了1024次

P1059 明明的随机数
排序去重

  1. 用set,去重
  2. 注意到a[i]范围很小,统计每个数字是否出现过
  3. sort unique
    sort(a, a + n);
    n = unique(a, a + n) - a;

unique 是不排序的

P1296 奶牛的耳语
二分/双指针法

  1. 二分
    统计每个数字p[i]前面有几个p[j]使得p[i]-p[j] <= d
    统计每个数字p[i]后面有几个p[j]使得p[j]-p[i] <= d

  2. 双指针法

P1138 第k小整数
排序

P1097 统计数字

  1. map
  2. 排序

P1051 谁拿了最多奖学金
模拟

P1080 国王游戏
按乘积排序

P1068 分数线划定
模拟

P1102 A-B 数对

  1. map
  2. 排序

P3913 车的攻击
行去重
列去重
sort 比 set 快很多

只是为了去重,而不需要有序的话,Hash也是很好的选择。

P4305 [JLOI2011]不重复数字
set暴力,
unordered_set
unordered_map
没有有序的性质了(不能lower_bound upper_bound)
不需要指定
出题人能不能造一个数据,让unordered_set超时?

P1469 找筷子
xor
(a xor a) == 0
(a xor b) == (b xor a)
(a xor b) xor c = a xor (b xor c)
如果不卡空间 map暴力 / 排序

P1866 编号
排序,算数

P1007 独木桥
P5835 [USACO19DEC]Meetings S
相遇调头

P1094 纪念品分组
双指针法

P1093 奖学金
模拟排序

P1626 象棋比赛
模拟排序

P4829 kry loves 2048
用堆/队列贪心,类似合并果子

P3918 [国家集训队]特技飞行
排序,最大的放两边

P2969 [USACO09DEC]Music Notes S
排序二分
1 1 2 3 3 3

  1. 前缀和,二分
    2 1 3

2 3 6
2. 排序所有询问,扫一遍

while (还有询问)
while (这一段在询问之前)
过掉
回答这个询问

P1271 【深基9.例1】选举学生会
计数排序

vector b[1000];
for (int i = 0; i < m; i++)
{
cin >> x >> name;
b[x].push_back(name);
}

P4378 [USACO18OPEN]Out of Sorts S
pair 带下标排序,排序

P4375 [USACO18OPEN]Out of Sorts G
pair 带下标排序,排序

归并排序

额外空间O(n)
数组等分为两份
递归排序
O(n)合并两个长度为n/2的有序数组
求逆序对的个数:有O(n log n)的做法
inplace_merge 原地归并排序
inplace_merge(a + l, a + m, a + r)
保证
a[l] a[l+1] .. a[m-1] 升序
a[m] a[m+1] .. a[r-1] 升序
合并2个序列

逆序对

归并排序可以用来求逆序对

快速排序

排序

额外空间O(log n)
每次随机取一个值,递归两边
第k大,如果不需要有序,只问第k大是什么 O(n)
问前k大?O(n + k log k)

O(n) 求第k大数

堆排序

额外空间 O(1)O(1) 的排序
建堆 O(n)O(n)
每次 pop 时间复杂度 O(logn)O(\log n)
总时间复杂度 O(nlogn)O(n \log n)

#include <bits/stdc++.h>
using namespace std;
int n, a[100020];
void up(int x) {
    while (x > 1) {
        if (a[x] > a[x / 2]) {
            swap(a[x], a[x / 2]);
            x /= 2;
        } else {
            break;
        }
    }
}
void down(int x) {
    while (2 * x <= n) {
        int p = 2 * x;
        if (p + 1 <= n && a[p + 1] > a[p]) {
            p++;
        }
        if (a[x] < a[p]) {
            swap(a[x], a[p]);
            x = p;
        } else {
            break;
        }
    }
}
int top() {
    // n >= 1
    return a[1];
}
void push(int x) {
    n++;
    a[n] = x;
    up(n);
}
void pop() {
    swap(a[1], a[n]);
    n--;
    down(1);
}
int main() {
    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d", &a[i]);
    }
    n = m;
    for (int i = m / 2; i > 0; i--) {
        down(i);
    }
    for (int i = m; i > 0; i--) {
        pop();
    }
    for (int i = 1; i <= m; i++) {
        printf("%d%c", a[i], i == m ? '\n' : ' ');
    }
}

结构体排序

CF659B Qualifying Contest

https://codeforces.com/problemset/problem/659/B
输入n个人,m个区域,每个人有名字,所属区域,得分
找到每个区域得分最高的两个人,如果第二和第三并列,输出?

CF810B Summer sell-off

https://codeforces.com/problemset/problem/810/B
输入一共n天,f天促销,每天备货数量,顾客数量,销量是备货数量和顾客数量的较小值
可以选择f天让备货数量乘以2,问最大销售数量之和是多少

CF976C Nested Segments

https://codeforces.com/problemset/problem/976/C
输入n个区间,从中找到2个区间i和j,使得l[i]<=l[j]&&r[j]<=r[i]

CF597B Restaurant

https://codeforces.com/problemset/problem/597/B
输入n个区间,问最多从中选几个区间,两两不相交,端点相交也不行

CF808C Tea Party

https://codeforces.com/problemset/problem/808/C
输入 n 个杯子的容量,和一共的茶水量 w,给每个杯子倒茶,要求
每个杯子必须装整数的茶水
每个杯子至少装一半的容量
所有茶水必须装在杯子中
容量大的杯子里的水不可以比容量小的杯子里的茶水少
给出任意一组合法答案,如果不存在合法方案,输出-1

CF230A Dragons

https://codeforces.com/problemset/problem/230/A
输入n个妖怪,和初始力量s,每个妖怪有力量x,和击败妖怪后自己力量增加值y
问能否击败n个妖怪,输出YES或NO

abc128_b Guidebook

https://atcoder.jp/contests/abc128/tasks/abc128_b
输入n个餐厅的所在城市和得分,按城市和得分排序,城市按字典序从小到大,分数按从大到小
输出排序之后的下标数组

abc116_d Various Sushi

https://atcoder.jp/contests/abc116/tasks/abc116_d
输入n个寿司,要从中选k个,每个寿司有种类和美味值
最终的收益是,选择的美味值之和,加上选择的种类数平方,输出最大的收益

进制转换

特殊情况:互相转的进制一个是另一个的次幂
10进制转10000进制,4位合1位
256进制转2进制,1位拆成8位

任意进制转换,2种做法
A进制转B进制

  1. 乘A,加B进制的下一位。(按照B进制来算)
  2. 模B,得到A进制的末位,再除以B(按照A进制来算)

16进制 FF 转成 10进制
(0 * 16 + 15) * 16 + 15 = 255

10进制 255 转成 2进制
255 % 2 = 1
255 / 2 = 127
...

abc220_b Base K

https://atcoder.jp/contests/abc220/tasks/abc220_b
输入进制K,和K进制下的A和B,输出A和B的乘积。

abc156_b Digits

https://atcoder.jp/contests/abc156/tasks/abc156_b
输入数字N和进制K,问数字N在K进制下有几位。

abc105_c Base -2 Number

https://atcoder.jp/contests/abc105/tasks/abc105_c
输入数字N,输出N在-2进制下的结果,负二进制。

CF1249C1 / CF1249C2 Good Numbers

https://codeforces.com/problemset/problem/1249/C1
https://codeforces.com/problemset/problem/1249/C2
一个数字如果是若干个不同的3的次幂的和,那么他是好数。
输入n,找到>=n最小的好数m。多组数据。

CF1110A Parity

https://codeforces.com/problemset/problem/1110/A
输入k位,b进制数,数组a。把数组a按b进制转成数字,问奇偶性。

CF552C Vanya and Scales

https://codeforces.com/problemset/problem/552/C
输入w和m,问能不能通过重量为w次幂的砝码,和天平称出m的重量。
天平左右都可以放砝码,每种砝码只有一个。

abc192_c Kaprekar Number

https://atcoder.jp/contests/abc192/tasks/abc192_c

定义 g1(x) 把 x 各位按降序排列
定义 g2(x) 把 x 各位按升序排列
定义 f(x) = g1(x) - g2(x)
比如 g1(314) = 431, g2(3021) = 123, f(271) = 721 - 127 = 594
输入n和k,执行 k 次 n = f(n) 之后 n 是多少?

P1143 进制转换

P1017 [NOIP2000 提高组] 进制转换

spoj THRPWRS

数学

P1134 [USACO3.2]阶乘问题
P1217 [USACO1.5]回文质数 Prime Palindromes
P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib
P2205 [USACO13JAN]Painting the Fence S
P2924 [USACO08DEC]Largest Fence G
P2926 [USACO08DEC]Patting Heads S
P3048 [USACO12FEB]Cow IDs S
P4187 [USACO18JAN]Stamp Painting G
P4909 [USACO06MAR]Ski Lift G
P5123 [USACO18DEC]Cowpatibility G
P5155 [USACO18DEC]Balance Beam P
P5834 [USACO19DEC]MooBuzz S
P5853 [USACO19DEC]Tree Depth P
P6218 [USACO06NOV] Round Numbers S
P6280 [USACO20OPEN]Exercise G

P2926 [USACO08DEC]Patting Heads S

1000000 / 1 + 1000000 / 2 + 1000000 / 3 + .. + 1000000 / 1000000 = 1000000 log 1000000

n / 2 + n / 3 + n / 5 + n / 7 + ... = n log log n

P6218 [USACO06NOV] Round Numbers S

answer of [1, r] - answer of [1, l-1]
answer of [1, r+1) - answer of [1, l)

how many round numbers < 0b101010

101000
1 0
1? C(1, 0)
1?? C(2, 0)
1??? C(3, 0) + C(3, 1)
1???? C(4, 0) + C(4, 1)
100??? C(3, 0) + C(3, 1) + C(3, 2)
10100? C(1, 0) + C(1, 1)

P3048 [USACO12FEB]Cow IDs S

00111
01011
01101
01110
10011
10101
10110
11001
11010
11100

C(4, 3) = 4
C(5, 3) = 10

数学

快速幂

x^17 = x^1 * x^16

x^1
x^2 = x^1 * x^1 % p
x^4 = x^2 * x^2 % p
x^8
x^16

https://en.wikipedia.org/wiki/Exponentiation_by_squaring

int power(int x, int y, int mod)
{
int re = 1;
for (; y; y >>= 1)
{
if (y & 1)
{
re = (long long)re * x % mod;
}
x = (long long)x * x % mod;
}
return re;
}

需要注意 x >= mod
第一次 x * x 可能越界
当 mod == 1, y == 0, 会直接返回 re = 1

P1226 【模板】快速幂||取余运算

注意一些特殊情况
越界和取模
int 2e9
long long 9e18

底数是1
底数是0
指数是0
指数是负数
模数是1

只要支持结合律,都可以快速幂优化

乘法
加法 long long * long long % long long
矩阵乘法
多项式乘法

出成题目最大的障碍,就是结果太大,取模

long long a = 12345789012345678
long long b = 22345789012345678
long long p = 32345789012345678
a * b % p

17 * a

1 * a + 16 * a

1 * a
2 * a
4 * a
8 * a
16 * a

long long mul(long long x, long long y, long long mod) // x * y % mod
{
long long re = 0;
for (; y; y >>= 1)
{
if (y & 1)
{
re = (re + x) % mod;
}
x = x * 2 % mod;
}
return re;
}

P3197 [HNOI2008]越狱

(pow(m, n, mod) - pow(m - 1, n - 1, mod) * m) % mod

https://en.wikipedia.org/wiki/Greatest_common_divisor
https://en.wikipedia.org/wiki/Pick's_theorem
int gcd(int x, int y) {
return y ? gcd(y, x % y) : x;
}

int lcm(int x, int y) {
return x / gcd(x, y) * y;
}

Pick's Theorem

P2735 [USACO3.4]网 Electric Fences

叉积

(0, 0)
(x1, y1)
(x2, y2)
三个点组成的三角形面积是
|x1 y2 - x2 y1| / 2

P1403 [AHOI2005]约数研究

1到n每个数字有多少个约数,求和
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (i % j == 0)
{
ans++;
}
}
}

1到n每个数字有多少个<=n的倍数,求和
for (int j = 1; j <= n; j++)
{
for (int i = j; i <= n; i++)
{
if (i % j == 0)
{
ans++;
}
}
}

for (int j = 1; j <= n; j++)
{
for (int i = j; i <= n; i += j)
{
ans++;
}
}

for (int j = 1; j <= n; j++)
{
ans += n / j;
}

abc172_d

https://atcoder.jp/contests/abc172/tasks/abc172_d

1到n每个数字有多少个约数,乘以i,求和
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (i % j == 0)
{
ans += i;
}
}
}

1到n每个数字有多少个<=n的倍数,乘以i,求和
for (int j = 1; j <= n; j++)
{
for (int i = j; i <= n; i++)
{
if (i % j == 0)
{
ans += i;
}
}
}

for (int j = 1; j <= n; j++)
{
for (int i = j; i <= n; i += j)
{
ans += i;
}
}

for (int j = 1; j <= n; j++)
{
ans += (n / j) * (n / j + 1) / 2 * j;
}

P2926 [USACO08DEC]Patting Heads S

所有 <= n 的自然数倒数之和,大约数 log n
log n = 1 / 1 + 1 / 2 + 1 / 3 + ... + 1/ n

所有 <= n 的质数倒数之和,大约数 log log n
log log n = 1 / 2 + 1 / 3 + 1/ 5 + 1 / 7 + .. + 1 / n

P1595 信封问题
错排问题

n=0
f[0] = 1

n=1
f[1] = 0

n = 2
2 1
f[2] = 1

n = 3
2 3 1
3 1 2
f[3] = 2

f[4] = 9
f[5] = 44

f[n] = (n-1) * (f[n-1] + f[n-2])

P2197 【模板】nim游戏
简单博弈 异或

P1965 转圈游戏
快速幂

乘法逆元

P2613 【模板】有理数取余
模质数
x 的逆元就是 pow(x, p - 2, p)
https://en.wikipedia.org/wiki/Fermat's_little_theorem
pow(x, p - 1, p) == 1
x * pow(x, p - 2, p) % p == 1

如果 x 和 p 互质
x^(p-1) % p == 1
x^n % p == x^(n % (p - 1)) % p

P1082 同余方程
模合数
https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
扩展欧几里得
a * x + b * y = gcd(a, b)
|x| + |y| 越小越好

输入 a 和 b,保证 a 和 b 互质
求a x % b == 1
a x + b y == 1

需要注意最终求出的 x 可能是负数

P3811 【模板】乘法逆元
O(n) 预处理1到n的逆元

  1. v[i] = (long long)v[p % i] * (p - p / i) % p;
  2. 求n!的逆元,倒序推出 (n-1)! (n-2)! ... 2! 1!
  3. 注意到逆元是完全积性函数,暴力计算所有质数的逆元,合数的逆元分解为两部分乘起来

P4071 [SDOI2016]排列计数
C(n, m) * derangement[n-m]

C(n, m)
n, m <= 1e6
mod prime

1 / 1 + 1 / 2 + 1 / 3 + ... + 1 / n = log n
1 / 2 + 1 / 3 + 1 / 5 + ... + 1 / n = log log n

O(n)O(\sqrt n) 判断数字是否质数

bool isPrime(int x)
{
    if (x < 2)
    {
        return false;
    }
    for (int i = 2; i <= x / i; i++)
    {
        if (x % i == 0)
        {
            return false;
        }
    }
    return true;
}

最简单的分解质因数
$O(\sqrt n)$ 分解质因数
```cpp
int p[20];
int a[20];
int k;
for (int i = 2; i * i <= n; i++)
{
    if (n % i == 0)
    {
        p[k] = i;
        while (n % i == 0)
        {
            a[k]++;
            n /= i;
        }
        k++;
    }
}
if (n > 1)
{
    p[k] = n;
    a[k] = 1;
    k++;
}

更高级的方法 Pollard's rho algorithm 时间复杂度是 O(n**(1/4))
一般用不到

可以用筛法求出1到n中的质数和每个数字的最小质因数
如果知道每个数字的最小质因数,那么可以很快的分解质因数

欧拉函数
欧拉定理

P5091 【模板】扩展欧拉定理

如果 x 和 p 互质
x^phi(p) % p == 1
x^n % p == x^(n % phi(p)) % p

如果 x 和 p 不互质,并且 n >= phi(p)
x^n % p == x^(n % phi(p) + phi(p)) % p

P1869 愚蠢的组合数
C(0, 0) = 1
C(0, 1) = 0
C(1, 0) = 1
C(1, 1) = 1

计算 C(n, m) % 2 相当于判断
是否存在一位 n是0, m是1
如果存在,答案是0
如果不存在,答案是1

if ((~ n & m) > 0) 答案是0

~
&
|
^

2个参数的位运算一共有16个

!
&&
||

https://en.cppreference.com/w/cpp/language/operator_alternative

P4702 取石子
P1495 【模板】中国剩余定理(CRT)/曹冲养猪
P4430 小猴打架
P4981 父子
P2290 [HNOI2004]树的计数

分块打表
123456789
123000000

多重集的全排列
a * 3
b * 4
c * 5
12! / 3! / 4! / 5!

int C(int n, int m)
{
int re = 1;
for (int i = 0; i < m; i++)
{
re = re * (n - i) / (i + 1);
}
return re;
}
C(6, 3)

Lucas's theorem
Legendre's formula

100! 末尾有几个 0

100 / 5 = 20
20 / 5 = 4
4 / 5 = 0

20 + 4 + 0

(100 - sum(400) / (5 - 1))

输入一个n
问1! 2! ... n! 中有多少个阶乘末尾有偶数个0
n <= 1e9

P2197 【模板】nim游戏
所有石子个数 xor
如果结果非0,先手必胜
如果结果是0,先手必败

CF784E Twisted Circuit
CF484A Bits
AT1157 2015
SP25844 MAXXOR - Find the max XOR value
P1100 高低位交换
P4310 绝世好题
P4144 大河的序列
P5390 [Cnoi2019]数学作业
P4932 浏览器
P2114 [NOI2014]起床困难综合症

CF1228C Primes and Multiplication
CF1178D Prime Graph
CF1195D2 Submarine in the Rybinsk Sea (hard edition)
CF1114C Trailing Loves (or L'oeufs?)
CF1107D Compression
CF1225D Power Products
CF1244C The Football Season
CF1220D Alex and Julian
CF1176D Recover it!
CF1166E The LCMs Must be Large
判断输入的集合是否存在一对没有交集

CF1101D GCD Counting
CF908E New Year and Entity Enumeration

高精度
分解质因数
乘法逆元
预处理阶乘逆元
预处理
组合数
Lucas定理
差分
gcd
exgcd

https://en.wikipedia.org/wiki/Euclidean_algorithm
https://en.wikipedia.org/wiki/Binary_GCD_algorithm
https://en.wikipedia.org/wiki/Bézout's_identity
https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm

__gcd

int gcd(int x, int y)
{
    return y > 0 ? gcd(y, x % y) : x;
}
int lcm(int x, int y)
{
    return x / gcd(x, y) * y; // to avoid overflow
    // not x * y / gcd(x, y);
    // 1000000000
    // 2000000000
}

a = 2, b = 0
find x, y
ax + by = 2

1 * 2 + 0 * 0 = 2
1 * 2 + 0 * (4 - 2 * 2) = 2
1 * 2 + 0 * 4 = 2
1 * (6 - 1 * 4) + 0 * 4 = 2
1 * 6 + (-1) * 4 == 2
1 * 6 + (-1) * (10 - 1 * 6) = 2
2 * 6 + (-1) * 10 = 2
2 * (46 - 4 * 10) + (-1) * 10 = 2
2 * 46 - 9 * 10 = 2
2 * 46 - 9 * (240 - 5 * 46) == 2
47 * 46 - 9 * 240 = 2

int exgcd(int &x, int &y, int a, int b)
{
    if (b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int g = exgcd(y, x, b, a % b);
    // y * b + x * (a % b) == g
    // y * b + x * (a - a / b * b) == g
    // y * b + x * a - a / b * x * b == g
    // (y - a / b * x) * b + x * a == g
    y -= a / b * x;
    return g;
}

https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
https://en.wikipedia.org/wiki/Fermat's_little_theorem
a ** (p-1) % p == 1
a ** (p-2) * a % p == 1
inv(a) = a ** (p-2) % p = pow(a, p - 2, p)

what if p is not a prime number?
https://en.wikipedia.org/wiki/Euler's_totient_function
https://en.wikipedia.org/wiki/Euler's_theorem

phi(p) = p-1
a ** phi(p) % p == 1
a ** (phi(p) - 1) * a % p == 1
inv(a) == (a ** (phi(p) - 1)) % p
the inverse of 7 when mod 36
pow(7, 11, 36) = 31
7 * 31 % 36 == 1

https://en.wikipedia.org/wiki/Integer_factorization
This is a very hard problem.
We usually try up to sqrt(n)

find x such that ax % p == 1

find x such that
a x + p y == 1
a and p are known, find x and y.
if we find x, them normalize it to [1, p-1]

https://en.wikipedia.org/wiki/Matrix_multiplication
definition

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        for (int k = 0; k < n; k++)
        {
            c[i][j] += a[i][k] * b[k][j];
        }
    }
}

Time Complexity: O(n^3)

A ** 10 = (A ** 8) * (A ** 2)
Because the numbers might be very large, we always mod something.

https://en.wikipedia.org/wiki/Fibonacci_number


a method about recursion

def query(n): // return (f[n], f[n+1])
    if n == 0:
        return (0, 1)
    if n is odd:
        query(n-1) // get f[n-1] and f[n]
        return (f[n], f[n-1]+f[n])
    if n is even:
        query(n / 2) // get f[n/2], f[n/2+1]
        f[n+1] = f[n/2]*f[n/2] + f[n/2+1]*f[n/2+1]
        f[n] = (f[n/2-1]+f[n/2+1]) * f[n/2]
        f[n] = (f[n/2+1] - f[n/2] + f[n/2+1]) * f[n/2]



Solving homogeneous linear recurrence relations with constant coefficients
F[k+1] F[k]
0 0

1 1
1 0

=
F[k]+F[k+1] F[k+1]
0 0

=
F[k+2] F[k+1]
0 0

F[1] [0]
0 0

B ** n

=

f[n+1] f[n]
0 0

https://www.luogu.com.cn/problem/P1349
P1349 广义斐波那契数列

f[i+1] f[i]
0 0

p 1
q 0

=

(pf[i+1]+qf[i]) f[i+1]
0 0

=
f[i+2] f[i+1]
0 0


初始知道的是
AT start, we know
f[2] f[1]

乘以转移矩阵n-1次之后得到
After multiplied by the transition matrix n-1 times
f[n+1] f[n]


f[2] f[1] -> f[n+1] f[n]

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P1349_.cpp

必须是 常系数线性递推 才可以 矩阵乘法优化
如果系数中出现了 i 之类的,就不能矩阵乘法优化
f[i] = i * f[i-1]
阶乘是不能优化的

常系数线性递推的例子
f[i] = f[i-1] + f[i-2]
f[i] = f[i-1] + f[i-2] + 1
f[i] = f[i-1] + f[i-2] + 1 + i + i * i

f[i] = a * f[i-1] + b * g[i-1]
g[i] = c * f[i-1] + d * g[i-1]

不是常系数线性递推的例子
f[i] = i * f[i-1]
f[i] = (i-1) * (f[i-1] + f[i-2])
f[i] = f[i-1] + sqrt(i)

https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
O(n/2 + n/3 + n/5 + n/7 + .. ) = O(n log log n)

1/2 + 1/3 + 1/5 + 1/7 + ... + 1/n = O(log log n)
Divergent series

https://en.wikipedia.org/wiki/Pick's_theorem
https://en.wikipedia.org/wiki/Cross_product
(x1 * y2 - x2 * y1)

https://en.wikipedia.org/wiki/Factorial

https://en.wikipedia.org/wiki/Combination

preprocess factorials and the inverse of factorials
C(n, m) % p, p is a prime number

p = 10007;
inv[1] = 1;
for (int i = 2; i <= n; i++)
{
inv[i] = (long long)(p - p / i) * inv[p % i] % p;
}

fac[0] = 1;
invfac[0] = 1;
for (int i = 1; i <= n; i++)
{
fac[i] = fac[i-1] * i % p;
invfac[i] = invfac[i-1] * inv[i] % p;
}

http://usaco.org/index.php?page=viewproblem2&cpid=1045

1 2 3 4 5
2 3 1 5 4

2 3 1 5 4
3 1 2 4 5

3 1 2 4 5

For a permutation, the LCM of lengths of cycles

Algorithm 1:
get all permutations, the LCM of lengths of cycles
2 methods

  1. DFS
  2. next_permutation
    do
    {

}
while(next_permutation(a, a + n));

Time Complexity O(n!)

Algorithm 2:
get all partitions of number n, which are the lengths of cycles
calculate the number of plans

Time Complexity O(the number of partitions of n)

Algorithm 3:
f[i][j]
the sum of numbers is i
the lcm of numbers is j

(but the lcm can be very large?, use map<int, int> )

Algorithm 4
Do algorithm 3 for each prime number, seperately
f[i][j]
the sum of numbers is i
the number of power is j (0 <= j <= 12)

n=5

(1, 0, 0)

Do p=2

(45, 45, 30)

45 partitions of n, in the LCM, the power of 2 is 0
45 partitions of n, in the LCM, the power of 2 is 1
30 partitions of n, in the LCM, the power of 2 is 2

ans *= pow(2 ** 0, 45)
ans *= pow(2 ** 1, 45)
ans *= pow(2 ** 2, 30)

Algorithm 5
Do algorithm 4, but change the order of k
f[i]
the sum of numbers is i
the number of power is j (0 <= j <= 12)

if the prime number is 3
n = 10
add 1 2 4 5 7 8 10
add 3 6
add 9

remove 9
remove 3 6

Algorithm 6
For these counting problems, we can not only add a number
but also remove a number

after add all numbers
f[i] is i!

(the number of plans whose power <= j) - (the number of plans whose power <= j - 1) ==
(the number of plans whose power == j)

# include <bits/stdc++.h>
<p class="mume-header " id="include-bitsstdch"></p>

using namespace std;
typedef long long ll;
int n, mod, modd;
ll fac[7520];
ll inv[7520];
ll invfac[7520];
int p[1000], pc;
typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
    ull b, m;
    FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
    ull reduce(ull a) {
        ull q = (ull)((L(m) * a) >> 64);
        ull r = a - q * b; // can be proven that 0 <= r < 2*b
        return r >= b ? r - b : r;
    }
}FM(2), FMD(2);
int isPrime(int x)
{
    if (x < 2)
    {
        return false;
    }
    for (int i = 2; i * i <= x; i++)
    {
        if (x % i == 0)
        {
            return false;
        }
    }
    return true;
}
ll pw(ll x, ll y, ll mod)
{
    ll re = 1;
    for (; y > 0; y >>= 1)
    {
        if (y & 1)
        {
            re = (re * x) % mod;
        }
        x = x * x % mod;
    }
    return re;
}
ll gcd(ll x, ll y)
{
    return y ? gcd(y, x % y) : x;
}
ll lcm(ll x, ll y)
{
    return x / gcd(x, y) * y;
}
ll P(int n, int m)
{
    assert(0 <= m && m <= n);
    return fac[n] * invfac[n - m] % mod;
}
int a[20];
int b[20];
long long gao0ans = 1;

// n x y
// n! / (n - x*y)! / pow(x, y) / y!
// C(n, x*y) * (x*y)! / pow(x, y) / y!
// G(n, x, y) = 
int d1[7520];
int d2[7520];
unsigned c[7520][7520];
vector<unsigned> cc[7520];
// c[i][j] = c[i-1][j] + c[i-1][j-1]
map<pair<int, int>, int> C[7520];
ll timecount = 0;
inline ll calc(int n, int x, int y)
{
    timecount++;
    return FMD.reduce((ull)c[n][x * y] * cc[x][y]);
    pair<int, int> u = make_pair(x, y);
    if (C[n].find(u) != C[n].end())
    {
        return C[n][u];
    }
    // ll ret = c[n][x * y];
    // for (int i = 1; i <= x * y; i++)
    // {
    // 	ret = ret * i;
    // 	assert(ret < 1e60);
    // }
    // for (int i = 1; i <= y; i++)
    // {
    // 	ret = ret / x;
    // 	ret = ret / i;
    // }


    for (int i = 1; i <= y; i++)
    {
        d1[i] = x;
        d2[i] = i;
    }
    ll re = c[n][x * y];
    for (int i = 1; i <= x * y; i++)
    {
        int k = i;
        for (int j = 1; j <= y; j++)
        {
            ll g;
            g = gcd(k, d1[j]);
            k /= g;
            d1[j] /= g;
            g = gcd(k, d2[j]);
            k /= g;
            d2[j] /= g;
            if (k == 1)
            {
                break;
            }
        }
        re = re * k % modd;
    }
    // ret %= modd;
    // if (re != ret)
    // {
    // 	cout << n << ' ' << x << ' ' << y << ' ' << ret << ' ' << re << endl;
    // }
    // assert(re == ret);
    C[n][u] = re;
    // cout << n << ' ' << x << ' ' << y << ' ' << re << endl;
    assert((long long)c[n][x * y] * cc[x][y] % modd == re);
    return re;
}

void dfs(int x, int y, int z)
{
    if (x == 0)
    {
        n = a[0] * b[0] + a[1] * b[1] + ... + a[z-1] * b[z-1]
        the number of a[i] is b[i]
        // 20 = 2 + 2 + 2 + 3 + 3 + 5 + 5
        // a[] = {2, 3, 5}
        // b[] = {3, 2, 2}
        there are  b[i]  a[i]s
        int l = 1;
        ll sum = 1;
        int cnt = 0;
        for (int i = 0; i < z; i++)
        {
            l = lcm(l, a[i]);
            cnt += a[i] * b[i];
            sum = sum * calc(cnt, a[i], b[i]) % modd;
        }
        gao0ans = gao0ans * pw(l, sum, mod) % mod;
        // for (int i = 0; i < z; i++)
        // {
        // 	printf("%d %d\n", a[i], b[i]);
        // }
        // printf("%lld %d\n", sum, cnt);
        // printf("------\n");
        return;
    }
    if (y == 0)
    {
        return;
    }
    dfs(x, y - 1, z);
    a[z] = y;
    for (b[z] = 1; b[z] * y <= x; b[z]++)
    {
        dfs(x - b[z] * y, y - 1, z + 1);
    }
    return;
}
int gao0()
{
    gao0ans = 1;
    dfs(n, n, 0);
    return gao0ans;
}


int gao1()
{
    map<ll, ll> f[7501];
    for (int i = 0; i <= n; i++)
    {
        f[i].clear();
    }
    f[0][1] = 1;
    // sum = 0, lcm = 1, the number of plans is 1
    for (int k = 1; k <= n; k++)
    {
        // try to add  some k s, (some cycles of length k)
        for (int i = n; i > 0; i--)
        {

            for (int l = 1; l * k <= i; l++)
            {
                // add l ks to (i-k*l), and get i
                // add l cycles of length k
                for (pair<ll, ll> j: f[i - k * l])
                {
                    // for all states
                    // j.first is the LCM, j.second is the number of plans
                    // f[i - k * l][j] -> f[i][lcm(k, j)]
                    // ll tmp = f[i - k * l][j.first] * P(i, k * l)
                    ll tmp = j.second;
                    // cout << tmp << ' ' << i << ' ' << k * l << ' ' << P(i, k * l) << endl;
                    tmp = tmp * calc(i, k, l) % modd;
                    ll &t = f[i][lcm(k, j.first)];
                    t = (t + tmp) % modd;
                }
            }
        }
    }
    ll ans = 1;
    for (pair<ll, int> j: f[n])
    {
        // cout << j.first << ' ' << j.second << endl;
        ans = ans * pw(j.first, j.second, mod) % mod;
    }
    return ans % mod;
}
ll f[7520][13];
int gao2()
{
    int g[7520] = {};
    ll ans = 1;
    for (int q = 0; q < pc; q++)
    {
        // cout << q[p] << endl;
        memset(f, 0, sizeof f);
        memset(g, 0, sizeof g);
        for (int i = 1; i <= n; i++)
        {
            int j = i;
            while (j % p[q] == 0)
            {
                g[i]++;
                j /= p[q];
            }
            // i = 100 = 2^2 * 5^2
            // p[q] = 2
            // g[100] = 2
            // g[256] = 8
            // g[i] is the power of p[q] in i
        }
        f[0][0] = 1;
        for (int k = 1; k <= n; k++)
        {
            // try to add  some k s, (some cycles of length k)
            for (int i = n; i > 0; i--)
            {
                for (int l = 1; l * k <= i; l++)
                {
                    // add l ks to (i-k*l), and get i
                    // add l cycles of length k
                    for (int j = 0; j < 13; j++)
                    {
                        // try the number of power

                        // f[i - k * l][j] -> f[i][max(g[k], j)]
                        // ll tmp = f[i - k * l][j] * P(i, k * l)
                        ll tmp = f[i - k * l][j];
                        // cout << tmp << ' ' << i << ' ' << k * l << ' ' << P(i, k * l) << endl;
                        tmp = tmp * calc(i, k, l) % modd;
                        ll &t = f[i][max(g[k], j)];
                        // use max instead of lcm
                        t = (t + tmp) % modd;
                    }
                }
            }
        }
        for (int j = 0; j < 13; j++)
        {
            ans = ans * pw(pw(p[q], j, mod), f[n][j], mod) % mod;
        }
    }
    return ans;
}

namespace gao3
{
    int f[7520];
    inline void backward(int k, int s)
    {
        for (int i = n % s; i <= n; i += s)
        {
            for (int l = 1; l * k <= i; l++)
            {
                // f[i] = (f[i] + modd - FMD.reduce((ull)f[i - k * l] * calc(i, k, l)));
                f[i] = (f[i] + modd - FMD.reduce(FMD.reduce((ull)f[i - k * l] * c[i][k*l]) * cc[k][l]));
                if (f[i] >= modd)
                {
                    f[i] -= modd;
                }
            }
        }
    }
    int gao3()
    {
        ll ans = 1;
        for (int q = 0; q < pc; q++)
        {
            // if (q < 4)
            // printf("LINE %d %d %.3fseconds\n", __LINE__, p[q], clock()/1e6);
            vector<int> g[14];
            // cout << q[p] << endl;
            memset(f, 0, sizeof f);
            f[0] = 1;
            int mc = 0;
            for (int i = 1; i <= n; i++)
            {
                f[i] = FMD.reduce((ull)f[i - 1] * i) % modd;
                int j = i;
                int c = 0;
                while (j % p[q] == 0)
                {
                    c++;
                    j /= p[q];
                }
                g[c].push_back(i);
                mc = max(mc, c);
            }
            ll lastf = f[n];
            for (int j = mc; j >= 1; j--)
            {
                for (int k: g[j])
                {
                    backward(k, p[q]);
                }
                ans = ans * pw(pw(p[q], j, mod), (lastf - f[n] + modd), mod) % mod;
                lastf = f[n];
            }
        }
        return ans;
    }
    // 枚举质数 \sum (n/质数 * n/质数 log n)
}

namespace gao4
{
    int f[7520];
    int g[7520];
    vector<int> divisors[14];
    inline void backward(int pk, int p, int ppk)
    {
        for (int i = n % p; i <= n; i += p)
        {
            for (int j = 1; j * ppk <= i; j++)
            {
                f[i] = (f[i] + modd - FMD.reduce(FMD.reduce((ull)f[i - j * ppk] * c[i][j * ppk]) * g[j]));
                if (f[i] >= modd)
                {
                    f[i] -= modd;
                }
            }
        }
    }
    int gao4()
    {
        ll ans = 1;
        for (int q = 0; q < pc; q++)
        {
            memset(f, 0, sizeof f);
            f[0] = 1;
            int mc = 0;
            for (int i = 0; i < 14; i++)
            {
                divisors[i].clear();
            }
            for (int i = 1; i <= n; i++)
            {
                f[i] = FMD.reduce((ull)f[i - 1] * i) % modd;
                int j = i;
                int c = 0;
                while (j % p[q] == 0)
                {
                    c++;
                    j /= p[q];
                }
                divisors[c].push_back(i);
                mc = max(mc, c);
            }
            ll lastf = f[n];
            for (int j = mc; j >= 1; j--)
            {
                int ppk = 1;
                for (int k = 0; k < j; k++)
                {
                    ppk *= p[q];
                }
                memset(g, 0, sizeof g);
                g[0] = 1;
                for (int i = 0; i * ppk <= n; i++)
                {
                    for (int j = 1; j <= i; j++)
                    {
                        if (j % p[q] == 0)
                        {
                            continue;
                        }
                        g[i] = (g[i] + FMD.reduce(FMD.reduce(c[i * ppk - 1][j * ppk - 1] * fac[j * ppk - 1]) * g[i - j]));
                        if (g[i] >= modd)
                        {
                            g[i] -= modd;
                        }
                    }
                }
                backward(j, p[q], ppk);
                // cout << p[q] << ' ' << j << ' ' << (lastf - f[n] + modd) % modd << endl;
                ans = ans * pw(pw(p[q], j, mod), (lastf - f[n] + modd), mod) % mod;
                lastf = f[n];
            }
        }
        return ans;
    }
    // 枚举质数 \sum (n/质数 * n/质数 log n)
}

int main()
{
    freopen("exercise.in", "r", stdin);
    freopen("exercise.out", "w", stdout);
    cin >> n >> mod;
    modd = mod - 1;
    FM = FastMod(mod);
    FMD = FastMod(mod - 1);
    fac[0] = 1;
    invfac[0] = 1;

    // C(i, i) = 1
    // C(i, 0) = 1
    // C(i, j) = 0  j>i 

    // C(n, m) = C(n-1,m-1) + C(n-1, m)
    for (int i = 0; i <= n; i++)
    {
        c[i][0] = 1;
        for (int j = 1; j <= n; j++)
        {
            c[i][j] = (c[i-1][j-1] + c[i-1][j]);
            if (c[i][j] >= modd)
            {
                c[i][j] -= modd;
            }
        }
    }
    // for (int i = 1; i <= n; i++)
    // {
    // 	cc[i].resize(n / i + 1);
    // 	cc[i][0] = 1;
    // 	for (int j = 1; i * j <= n; j++)
    // 	{
    // 		cc[i][j] = cc[i][j - 1];
    // 		for (int k = i * j - i + 1; k < i * j; k++)
    // 		{
    // 			cc[i][j] = FMD.reduce((ull)cc[i][j] * k);
    // 		}
    // 	}
    // }

    for (int i = 1; i <= n; i++)
    {
        if (isPrime(i))
        {
            p[pc++] = i;
        }
        fac[i] = fac[i - 1] * i % modd;
        // inv[i] = pw(i, mod - 2, mod);
        // invfac[i] = pw(fac[i], mod - 2, mod);
    }
    // printf("%d\n", gao0());
    // printf("%d\n", gao1());
    // printf("%d\n", gao2());
    // printf("LINE %d %.3fseconds\n", __LINE__, clock()/1e6);
    // printf("%d\n", gao3::gao3());
    // printf("LINE %d %.3fseconds\n", __LINE__, clock()/1e6);
    printf("%d\n", gao4::gao4());
    // printf("LINE %d %.3fseconds\n", __LINE__, clock()/1e6);
    // printf("%lld\n", timecount);
    // printf("LINE %d %.3fseconds\n", __LINE__, clock()/1e6);
    return 0;
}
/*
把n分成若干部分

n = 12

n = 2 + 2 + 2 + 3 + 3

方案数
12! / 2 / 2 / 2 / 3 / 3 / 3! / 2!

6! / 2 / 2 / 2 / 3! * P(12, 6) / 3 / 3 / 2!


// n x y
// n! / (n - x*y)! / pow(x, y) / y!



sqrt(7500)一下的质数,暴力去记录次幂,以上的,依次处理


2   0..12
3   0..8
5   0..5
7   0..4
11  0..3
13  0..3
17  0..3
19  0..3
23  0..2
29  0..2
31  0..2
37  0..2
41  0..2
43  0..2
47  0..2
53  0..2
59  0..2
61  0..2
67  0..2
71  0..2
73  0..2
79  0..2
83  0..2


f[i][j]
当前加到k数字
所有数字之和是i
所有数字的lcm是j
方案数是...
*/

after adding 1
f[ ] are
1 0 0 {}
1 0 0 {1}
1 0 0 {1, 1}
1 0 0 {1, 1, 1}
1 0 0 {1, 1, 1, 1}
1 0 0 {1, 1, 1, 1, 1}

after adding 2
f[ ] are
1 0 0 {}
1 0 0 {1}
1 1 0 {1, 1} {2}
1 3 0 {1, 1, 1} {(1, 2) (3)} {(1, 3) (2)} {(2, 3) (1)}
1 9 0 {1, 1, 1, 1}
{1, 1, 2} 4! / 2 / 2! = 6 {(1, 2), (3), (4)} {(1, 3), (2), (4)} {(1, 4), (2), (3)} ...
{2, 2} 4! / 2 / 2 / 2! = 3 {(1, 2), (3, 4)} {(1, 3), (2, 4)} {(1, 4), (2, 3)}

1 25 0 {1, 1, 1, 1, 1}
{1, 1, 1, 2} 5! / 2 / 3! = 10
{1, 2, 2} 5! / 2 / 2 / 2! = 15

We have
f[] = 1 1 1 3 9 45

We want to get
g[] = 1 1 2 6 18 90

g[0] = f[0]
g[1] = f[1]
g[2] = f[2] + f[0] * ...
g[3] = f[3] + f[1] * ...
g[4] = f[4] + f[2] * ... + f[0] * ...
g[5] = f[5] + f[3] * ... + f[1] * ...

f[5] = f[5] + f[3] * ... + f[1] * ...
f[4] = f[4] + f[2] * ... + f[0] * ...
f[3] = f[3] + f[1] * ...
f[2] = f[2] + f[0] * ...
f[1] = f[1]
f[0] = f[0]

now we have g
we want to get f

f[0] = g[0]
f[1] = g[1]
f[2] = g[2] - f[0] * ...
f[3] = g[3] - f[1] * ...
f[4] = g[4] - f[2] * ... - f[0] * ...
f[5] = g[5] - f[3] * ... - f[1] * ...

f[0] = f[0]
f[1] = f[1]
f[2] = f[2] - f[0] * ...
f[3] = f[3] - f[1] * ...
f[4] = f[4] - f[2] * ... - f[0] * ...
f[5] = f[5] - f[3] * ... - f[1] * ...

https://atcoder.jp/contests/arc057/tasks/arc057_d
https://atcoder.jp/contests/arc057/submissions/2640690

https://en.wikipedia.org/wiki/Faulhaber's_formula
the sum of the m-th powers of the first n positive integers
Time Complexity (m^2)

best algorithm O(m)
https://en.wikipedia.org/wiki/Lagrange_polynomial

https://www.spoj.com/problems/TREESUM (Easy Version)
https://en.wikipedia.org/wiki/Binomial_theorem

http://acm.hdu.edu.cn/showproblem.php?pid=4625
https://en.wikipedia.org/wiki/Stirling_number
https://en.wikipedia.org/wiki/Stirling_numbers_of_the_first_kind
https://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind
https://en.wikipedia.org/wiki/Falling_and_rising_factorials

https://en.wikipedia.org/wiki/Gaussian_elimination

https://en.wikipedia.org/wiki/Invertible_matrix

https://en.wikipedia.org/wiki/Determinant
https://en.wikipedia.org/wiki/Dot_product

https://en.wikipedia.org/wiki/Kirchhoff's_theorem
https://en.wikipedia.org/wiki/Lindström–Gessel–Viennot_lemma

https://en.wikipedia.org/wiki/Horner's_method
秦九韶算法

https://en.wikipedia.org/wiki/Pythagorean_theorem
勾股定理

https://en.wikipedia.org/wiki/Derangement

https://en.wikipedia.org/wiki/Chinese_remainder_theorem

https://en.wikipedia.org/wiki/Prüfer_sequence
https://en.wikipedia.org/wiki/Cayley's_formula

https://en.wikipedia.org/wiki/Inclusion–exclusion_principle

https://en.wikipedia.org/wiki/Nim
https://en.wikipedia.org/wiki/Chomp

https://en.wikipedia.org/wiki/Möbius_function

https://en.wikipedia.org/wiki/Birthday_problem
https://en.wikipedia.org/wiki/Monty_Hall_problem
https://en.wikipedia.org/wiki/Boy_or_Girl_paradox


https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
https://en.wikipedia.org/wiki/Pollard's_rho_algorithm

https://en.wikipedia.org/wiki/IEEE_754

https://en.wikipedia.org/wiki/Discrete_logarithm
https://en.wikipedia.org/wiki/Baby-step_giant-step
https://en.wikipedia.org/wiki/Fast_Fourier_transform
https://en.wikipedia.org/wiki/Generating_function
https://en.wikipedia.org/wiki/Taylor_series
https://en.wikipedia.org/wiki/Newton's_method
https://en.wikipedia.org/wiki/Gray_code
https://en.wikipedia.org/wiki/Berlekamp–Massey_algorithm
https://en.wikipedia.org/wiki/Cayley–Hamilton_theorem

Math Problems
http://projecteuler.net/

https://www.spoj.com/problems/FIBHARD
Fibonacci mod p has a period
f[n] % 998244353
f[n] % 998244353 = f[n - 1996488708] % 998244353
f[n] % 998244353 = f[n % 1996488708] % 998244353

http://www.usaco.org/index.php?page=viewproblem2&cpid=1018
https://www.luogu.com.cn/problem/P6146
P6146 [USACO20FEB]Help Yourself G

c[i] is the number of segments cover the left endpoint of the i-th segment.

the i-th segment increases the answer by 2 ** (n-1-c[i])

http://usaco.org/index.php?page=viewproblem2&cpid=997
https://www.luogu.com.cn/problem/P6009
P6009 [USACO20JAN]Non-Decreasing Subsequences P

http://www.usaco.org/index.php?page=viewproblem2&cpid=116
https://www.luogu.com.cn/problem/P3048
P3048 [USACO12FEB]Cow IDs S

Find the n-th integer, whose binary representation has m 1s

The number of interges < n, whose binary representation has m 1s

These 2 problems can be converted to each other by binary search

https://www.luogu.com.cn/problem/P5123
http://www.usaco.org/index.php?page=viewproblem2&cpid=862
P5123 [USACO18DEC]Cowpatibility G

map<vector, int> g;

ans = n * (n - 1) / 2
for each set with size 1, S
if the number of i, such that S is a subset of A[i], is cnt
ans -= C(cnt, 2);

for each set with size 2, S
if the number of i, such that S is a subset of A[i], is cnt
ans += C(cnt, 2);

for each set with size 3, S
if the number of i, such that S is a subset of A[i], is cnt
ans -= C(cnt, 2);

for each set with size 4, S
if the number of i, such that S is a subset of A[i], is cnt
ans += C(cnt, 2);

for each set with size 5, S
if the number of i, such that S is a subset of A[i], is cnt
ans -= C(cnt, 2);

{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}

[A B C D E]
[A B C D E]

compute the pair of sets, whose intersection's size is exactly 1
size=0, +1, -0
size=1, -1, +1
size=2, +1, -2
size=3, -1, +3
size=4, +1, -4
size=5, -1, +5

[A, B]

+= 3 * 1
-= 3 * 2

[A, B, C]

http://www.usaco.org/index.php?page=viewproblem2&cpid=864
https://www.luogu.com.cn/problem/P5155
P5155 [USACO18DEC]Balance Beam P

https://www.luogu.com.cn/problem/SP25844
SP25844 MAXXOR - Find the max XOR value

2 1

2 3 1
3 1 2

n=0 1
n=1 0
n=2 1
n=3 2
n=4 9
n=5 44
n=6 265

非线性递推
f[n] = (f[n-1] + f[n-2])*(n-1)
f[n] = f[n-1]*n + (-1)^n

线性递推
f[i] = f[i-1] + f[i-2]

http://oeis.org/

Catalan Number
C(2n, n) - C(2n, n + 1) = C(2n, n) / (n + 1)

f[1] = 1
f[2] = 2
f[3] = 5
f[4] = 14

  1. n个左括号,n个右括号,有多少种合法的括号序列
  2. 等价于有多少种进出栈序列
  3. n个节点有多少个二叉树
  4. 正n边形,连接对角线(不能相交)可以把n边形分成n-2个三角形,问方案数
  5. 2n个元素排成一个环,两两配对,不能相交,问有多少种方法
  6. 一行n个元素,分成若干组,组和组不能相交

( 入栈
) 出栈

入栈顺序 1 2 3

((())) 3 2 1
()()() 1 2 3
(()()) 2 3 1
()(()) 1 3 2
(())() 2 1 3

不可能出现 3 1 2

从(1, -1)到(n, n)任意一个方案
对应
从(0, 0)到(n, n)任意一个不合法的方案

从(0, 0)到(n, n)方案数C(2n, n)
从(1, -1)到(n, n)方案数C(2n, n-1)
总方案数C(2n, n) - C(2n, n-1) = C(2n, n)/(n+1)

f[n] = f[n-1] * (2n-1) * (2n) / (n) / (n+1)
f[n] = f[n-1] * (4n-2) / (n+1)

一行n个元素
分成若干组,
组和组不能相交

i < j < k < l

a[i] == a[k] && a[j] == a[l]

1 2 1 2 不可以的
1 2 1 3 1 4 1 可以的

组合数
re = 1
for (int i = 0; i < m; i++)
{
re = re * (n - i) / (i + 1);
}
return re;

Lucas's Theorem

15 = 1111
5 = 0101
C(15, 5) % 2 = C(1, 0) * C(1, 1) * C(1, 0) * C(1, 1);

C(0, 0) = 1
C(0, 1) = 0
C(1, 0) = 1
C(1, 1) = 1

C(n, m) 的奇偶性是否存在一位n是0,m是1

C(16, 5) 其中2的次幂是多少呢?

C(n, m)
m和n-m都转化为2进制,相加,问发生了几次进位,就是2的次幂是多少
5 = 0101
11 = 1011

c[i][j] = c[i-1][j] + c[i-1][j-1]

左移 (乘以2的次幂)低位补0
右移 (除以2的次幂)高位补符号位

计算机内部怎么存整数

位运算 & | ^

builtin函数以l和ll后缀的区别是什么,
不加int
加l是long
加ll是long long

__builtin_popcount 1的个数
__builtin_parity 1的个数的奇偶性
__builtin_clz count leading zero 前导0的个数
__builtin_ctz count trailing zero 后缀0的个数

如果不用builtin函数怎么做?
能不能预处理所有数字中有几个1/1的个数的奇偶性

{} 0
{1} 1
{2} 2
{3} 3
{1, 2} 3
{1, 3} 2
{2, 3} 1
{1, 2, 3} 0

12
按位做,看哪些位上出现过1

MAXXOR
(1 << (32 - __builtin_clz(L ^ R))) - 1

itoa
C++转进制

m的第i位

if (m >> i & 1)
{

}

CF484A Bits
ans = l
把ans的最后一个0改成1 如果<=r继续改

ans |= ans + 1

x & (x - 1)
把最后一个1改成0
x | (x + 1)
把最后一个0改成1

(x&y) + (x|y) > max(x, y) * 2

每一位有4种情况

  1. 初始0最终是0,初始1最终是0
  2. 初始0最终是0,初始1最终是1
  3. 初始0最终是1,初始1最终是0
  4. 初始0最终是1,初始1最终是1

快速幂

问题

输入x,n,px, n, p,计算xnmodpx ^ n \bmod p

基本算法

一个暴力的做法就是直接把nnxx乘起来,时间复杂度为O(n)O(n)

考虑一个递归的计算方法:
ParseError: KaTeX parse error: Undefined control sequence: \mbox at position 27: …gin{cases} 1,&{\̲m̲b̲o̲x̲{如果}}n=0\\ x\,(…
每次递归nn都会变为原先的一半,所以时间复杂度为O(logn)O(\log n)

对于非递归的做法,考虑nn的二进制表示bkbk1,b0b_k b_{k-1} \cdots, b_0
xn=bkx2k×bk1x2k1××b0x20x^n = b_k x^{2^k} \times b_{k-1} x^{2^{k-1}} \times \cdots \times b_0 x^{2^0}
对于x20,x21,,x2kx^{2^0}, x^{2^1}, \ldots, x^{2^k}每一项是前面一项的平方,
可以在O(k)O(k)的时间内计算,然后再根据bib_i的值,
决定每个x2ix^{2^i}是否乘入答案中。

代码

递归的实现

long long pow(long long x, long long n, long long p) {
    if (n == 0) {
        return 1;
    } else {
        int t = pow(x * x % p, n / 2, p);
        if (n % 2 == 1) {
            t = t * x % p;
        }
        return t;
    }
}

非递归的实现

int pow(int x, int n, int p) {
    int re = 1;
    for (; n > 0; n >>= 1) {
        if (n % 2 == 1) {
            re = (long long)re * x % p;
        }
        x = (long long)x * x % p;
    }
    return re;
}
  1. 一般使用非递归版本较多。
  2. 在数论和计数问题中,一般认为00=10^0 = 1.
  3. nn为负数,会导致程序出错。
  4. 如果p=1,n=0p = 1, n = 0,那么函数会返回11,而应该返回00,除了极少数坑人的题目,一般并不需要考虑这种情况。
  5. 如果pp的范围超过了int,那么需要额外实现一个long long * long long % long long 的函数,见推广部分。
  6. x可能大于p,在一些情况中,这会导致第一次x * x % p越界。
  7. 如果不考虑代码风格,可以把(n % 2 == 1)替换为(n & 1)

其他语言

Python语言中的pow函数,可以直接计算整数的快速幂,非常适合用来做手速题。(其实你只需要在自己的模板中实现出快速幂即可)

推广

在一些情况中快速幂的nn可能非常大。

  1. 如果nn是输入的高精度数字(一般为十进制)那么并不需要进行每次模二,除以二的快速幂;可以用十进制快速幂。
  2. 如果是通过其他方式计算的,比如希望计算pow(x, pow(y, n), p)可以先通过找循环节的方式把指数部分取模pow(x, pow(y, n), p) == pow(x, pow(y, n, p - 1), p)

这个算法对于所有有结合律的运算均可以优化,其他常见的如下

  1. 整数
  2. 矩阵
  3. 多项式
  4. 排列/置换

题目

Luogu P1226
快速幂模板题,但是需要考虑很多特殊情况。

bzoj 4475
答案是pow(2, k * n, p)

bzoj 1008
全部减去不会发生越狱的。

参考资料

Wikipedia Exponentiation by squaring

spoj NDT

多项式

n次多项式,n+1个系数
(a[0] + a[1] * x + a[2] * x^2 + .. + a[n] * x^n)
(b[0] + b[1] * x + b[2] * x^2 + .. + b[n] * x^n)

A * B = C
c[0] = a[0] * b[0]
c[1] = a[1] * b[0] + a[0] * b[1]
c[2] = a[2] * b[0] + a[1] * b[1] + a[0] * b[2]
...

for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
{
c[i + j] += a[i] * b[j];
}
}

for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
c[(i * j) % n] += a[i] * b[j];
}
}

for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
c[i | j] += a[i] * b[j];
}
}

for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
c[i ^ j] += a[i] * b[j];
}
}
暴力 O(n^2)
FFT O(n log n)

离散傅里叶变换
连续傅里叶变换(和我们做的比赛没什么关系)

复数

找到l个点,l是2的次幂,对2个多项式求值

通过一个算法 求值
A(0) == ..
A(1) == ..
..
A(l-1) == ..

B(0) == ..
B(1) == ..
..
B(l-1) == ..

对应的位置乘起来
C(0) == A(0) * B(0)

C(i) == A(i) * B(i)
..

根据
C(0) C(1) C(...)
推回多项式的系数

复数的乘法
模长相乘,幅角相加

2类

  1. 用复数 / 用double FFT

  2. 用数论 / 取模,用原根 NTT

a[0] + a[1] x + a[2] x^2 + a[3] x^3

w_n 是 n次单位根 (w_n)^n = 1 以下用 w
n = 4
w^n = 1
w^{n/2} = -1

b[0] = A(w^0) = a[0] * w^0 + a[1] * w^0 + a[2] * w^0 + a[3] * w^0
b[1] = A(w^1) = a[0] * w^0 + a[1] * w^1 + a[2] * w^2 + a[3] * w^3
b[2] = A(w^2) = a[0] * w^0 + a[1] * w^2 + a[2] * w^0 + a[3] * w^2
b[3] = A(w^3) = a[0] * w^0 + a[1] * w^3 + a[2] * w^2 + a[3] * w^1

a[0] = (b[0] * w^0 + b[1] * w^0 + b[2] * w^0 + b[3] * w^0) / 4
a[3] = (b[0] * w^0 + b[1] * w^1 + b[2] * w^2 + b[3] * w^3) / 4
a[2] = (b[0] * w^0 + b[1] * w^2 + b[2] * w^0 + b[3] * w^2) / 4
a[1] = (b[0] * w^0 + b[1] * w^3 + b[2] * w^2 + b[3] * w^1) / 4

a[0]
a[2]
a[1]
a[3]

a[0] * w^0 + a[2] * w^0
a[0] * w^0 + a[2] * w^2
a[1] * w^0 + a[3] * w^0
a[1] * w^0 + a[3] * w^2

a[0] * w^0 + a[2] * w^0 + a[1] * w^0 + a[3] * w^0
a[0] * w^0 + a[2] * w^2 + a[1] * w^1 + a[3] * w^3 = a'[1] + a'[3] * w
a[0] * w^0 + a[2] * w^0 + a[1] * w^2 + a[3] * w^2
a[0] * w^0 + a[2] * w^2 + a[1] * w^3 + a[3] * w^1 = a'[1] - a'[3] * w

n=8
A(w^0) = a[0] * w^0 + a[1] * w^0 + a[2] + a[3] + a[0]
A(w^1) = a[0] + a[1] * w + a[2] * w^2 + a[3] * w^3
A(w^2) = a[0] + a[1] * w^2 + a[2] + a[3] * w^2
A(w^3) = a[0] + a[1] * w^3 + a[2]^2 + a[3] * w
A(w^4) = a[0] + a[1] * w^3 + a[2]^2 + a[3] * w
A(w^5) = a[0] + a[1] * w^3 + a[2]^2 + a[3] * w
A(w^6) = a[0] + a[1] * w^3 + a[2]^2 + a[3] * w
A(w^7) = a[0] + a[1] * w^3 + a[2]^2 + a[3] * w

a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]

1 -> 001
4 -> 100

a[0]
a[4]
a[2]
a[6]
a[1]
a[5]
a[3]
a[7]

a[0] * w^0 + a[4] * w^0
a[0] * w^0 + a[4] * w^4
a[2] * w^0 + a[6] * w^0
a[2] * w^0 + a[6] * w^4
a[1] * w^0 + a[5] * w^0
a[1] * w^0 + a[5] * w^4
a[3] * w^0 + a[7] * w^0
a[3] * w^0 + a[7] * w^4

a' 表示上一组

a[0] * w^0 + a[4] * w^0 + a[2] * w^0 + a[6] * w^0 (a'[0] + a'[2] * w^0)
a[0] * w^0 + a[4] * w^4 + a[2] * w^2 + a[6] * w^6 (a'[1] + a'[3] * w^2)
a[0] * w^0 + a[4] * w^0 + a[2] * w^4 + a[6] * w^4 (a'[0] + a'[2] * w^4)
a[0] * w^0 + a[4] * w^4 + a[2] * w^4 + a[6] * w^2 (a'[1] + a'[3] * w^6)

http://lydsy.com/JudgeOnline/problem.php?id=3513
x^j的系数,是j在a数组中出现的次数

给定n个长度分别为a_i的木棒,问随机选择3个木棒能够拼成三角形的概率。
问概率 = 问方案数
全部 - 不能
不能 = (最大的 >= 较小的2个)
枚举最大的是谁,在所有中选取不同的2个,不考虑顺序,和小于等于最大的方案数

(.. x^j ..)^2 之后的 j 次方的系数就是从a数组中选取2个数字(考虑顺序,可以重复)和为j的方案数

http://www.51nod.com/Challenge/Problem.html#problemId=1030
高精度进制转换

bzoj 3160
万径人踪灭

bzoj 5217
航海舰队

bzoj 3992
序列统计

http://poj.openjudge.cn/campus2014/D/
Colorful World

n = 4

a[0] a[3] a[2] a[1]
a[1] a[0] a[3] a[2]
a[2] a[1] a[0] a[3]
a[3] a[2] a[1] a[0]

a[0] a[1] a[2] a[3]

p[0] p[1] p[2] p[3]

q[0] = a[0] * p[0]
q[1] = a[0] * p[1] + a[1] * p[0]
q[2] = a[0] * p[2] + a[1] * p[1] + a[2] * p[0]
q[3] = a[0] * p[3] + a[1] * p[2] + a[2] * p[1] + a[3] * p[0]
q[4] = a[1] * p[3] + a[2] * p[2] + a[3] * p[1]
q[5] = a[2] * p[3] + a[3] * p[2]
q[6] = a[3] * p[3]

q[0] = a[0] * p[0] + a[1] * p[3] + a[2] * p[2] + a[3] * p[1]
q[1] = a[0] * p[1] + a[1] * p[0] + a[2] * p[3] + a[3] * p[2]
q[2] = a[0] * p[2] + a[1] * p[1] + a[2] * p[0] + a[3] * p[3]
q[3] = a[0] * p[3] + a[1] * p[2] + a[2] * p[1] + a[3] * p[0]

经过
(1, 1) (2, 8) (3, 27) 的二次多项式是什么
x^3 % ((x-1)(x-2)(x-3)) 是什么?

https://www.codechef.com/problems/FARASA
数多少个不同的部分和,根据数据大小分类讨论

https://www.codechef.com/problems/COUNTARI
数i, j, k等差数列个数,分块FFT

https://www.codechef.com/problems/PRIMEDST
树上距离为质数的点对

https://www.spoj.com/problems/OVICUMSUM/
前缀和 FFT 组合数

luogu P5488
前缀和 FFT 组合数

A * B * C

FFT(A, 1)
FFT(B, 1)
AB = A * B
FFT(C, 1)
ABC = AB * C
FFT(ABC, -1)

位运算

•【3】位运算:与(&)、或(|)、非(~)、 异或(^)、左移、右移 •【1】数的进制:二进制、八进制、十六进制和十进制及其转换 格雷码

有符号数处理 (signed number representations)

二进制

0:0
1:1
10:2
11:3
...

为了表达方便,补齐到 8 位

二进制

00000000:0
00000001:1
00000010:2
00000011:3
00000100:4
00000101:5
...
11111111:255

原码 (sign-and-magnitude)
存-x,最高位是1,其他位是x的表示法

反码(ones' complement)
存-x,x的表示法取反
反码存在-0的问题

补码(two's complement)
存负数用补码

移码(offset binary)
用不到,用全0表示最小的二进制数,全1表示最大的二进制数

二进制

00000000:0
00000001:1
00000010:2
00000011:3
00000100:4
00000101:5
01111111:127
10000000:-128
10000001:-127
...
11111110:-2
11111111:-1

如果 x 是正数,那么 -x 的补码是
x的二进制取反加一

00000011: 3
取反
11111100:-4
加一
11111101:-3

补码的好处:计算加减法不需要考虑正负数

原码和反码只有理论意义

取反 (bitwise NOT, complement)

0 变成 1
1 变成 0

NOT 0111 (十进制 7)
= 1000 (十进制 8)

NOT 10101011 (十进制 171)
= 01010100 (十进制 84)

位与 (bitwise AND)

同时是1,结果是1
否则是0

    0101 (decimal 5)
AND 0011 (decimal 3)
  = 0001 (decimal 1)

位或 (bitwise OR)

同时是0,结果是0
否则是1

   0101 (decimal 5)
OR 0011 (decimal 3)
 = 0111 (decimal 7)

位异或 (bitwise XOR)

相同为0,不同为1
二进制不进位加法

    0101 (decimal 5)
XOR 0011 (decimal 3)
  = 0110 (decimal 6)

异或在C++中的符号是^,这个符号不表示乘方

异或 和 加法很类似,有结合律,交换律
异或的逆运算也是异或,如果(a^b)==c那么a==(c^b)并且b==(c^a)
一个数字异或自己一定为0,(x^x)==0

左移

左移x位 相当于 乘以2的x次方
当然移动后可能超过int(或long long)的范围

左移,在最右边补0

左移位数不能超过类型本身的位数

int x = 1;
cout << (x << 32) << endl;

这是未定义行为,这样无法得到想要的结果

右移

右移x位 相当于 除以2的x次方,向下取整(一般的除法是向零取整)

右移,在最左边补符号位(负数补1,非负数补0),无符号类型补0

右移位数不能超过类型本身的位数

int x = 1;
cout << (x >> 32) << endl;

这是未定义行为,这样无法得到想要的结果

#include <bits/stdc++.h>
using namespace std;
int main()
{
    cout << (1 << 32) << endl;
    cout << (1 >> 32) << endl;
    int x = 1;
    cout << (x << 32) << endl;
    cout << (x >> 32) << endl;
    return 0;
}

在我的计算机上,前两个输出0,后两个输出1

内置函数

__builtin_popcount
__builtin_clz
__builtin_ctz

题目列表

P1469 找筷子

所有数字异或

取反!

非0变成0,0变成1
!0结果是1
!1结果是0
!2结果是0
!!2结果是1

一般不区分 1 和 true
一般不区分 0 和 false

逻辑与 && and

如果其两个变量的真值都为真,其结果为真,否则其结果为假。

逻辑或 || or

如果其两个变量中有一个真值为真,其结果为真,两个变量同时为假,其结果为假。

二进制

整数部分,把十进制转成二进制一直分解至商数为0。读余数从下读到上,即是二进制的整数部分数字。
比如 十进制23 是 二进制10111

23 / 2 = 11 余 1
11 / 2 = 5 余 1
5 / 2 = 2 余 1
2 / 2 = 1 余 0
1 / 2 = 0 余 1

从下往上读是 10111

按位取反 ~ compl

按位取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字1成为0,0成为1。
在计算机中,比如 int 类型,一共有 32 位,前面的位需要补 0
比如 255 的二进制是

 255 = 00000000 00000000 00000000 11111111
~255 = 11111111 11111111 11111111 00000000 = 4294967040 = -256

补码

补码是一种用二进制表示有符号数的方法,也是一种将数字的正负号变号的方式,常在计算机科学中使用。
正数和0的补码就是该数字本身。负数的补码则是将其对应正数按位取反再加1。
补码系统的最大优点是可以在加法或减法处理中,不需因为数字的正负而使用不同的计算方式。

按位与& bitand

按位与是双目运算符。
其功能是参与运算的两数各对应的二进位相与。
只有对应的两个二进位都为1时,结果位才为1。

3 0011
5 0101
&
1 0001

按位或| bitor

按位或是双目运算符。
其功能是参与运算的两数各对应的二进位相或。
只要对应的二个二进位有一个为1时,结果位就为1。

3 0011
5 0101
|
7 0111

异或^ xor

异或是双目运算符。
其功能是参与运算的两数各对应的二进位做不进位加法。
也可以理解为相同为0,不同为1。

3 0011
5 0101
^
6 0110

左移 <<

二进制向左移动,注意对于 int 类型一共有 32 位,移出界的会消失,右侧补 0

255 << 2 = 255 * 4 = 1020

左移 x 位相当于 乘以 2 的 x 次方

右移 >>

二进制向右移动,注意对于 int 类型一共有 32 位,移出界的会消失,左侧补符号位

1023 >> 2 = 255
-1 >> 1 = -1

右移 x 位相当于 除以 2 的 x 次方 下取整
注意区分下取整和向零取整

格雷码

8位

11111100 -4
11111101 -3
11111110 -2
11111111 -1
00000000 0
00000001 1
00000010 2
00000011 3

11111111 255

00000001

257

-x = (~ x) + 1
-~x = x + 1
~-x = x - 1

x & (~x + 1)

x=
110100
~x
001011
~x + 1
001100

least significant bit
lowbit(i)

i的最大的2的次幂的约数,i转二进制,最低哪一位是1

lowbit(i) = (i & -i)
(i - lowbit(i)) == (i & (i-1))
lowbit(12) = 4
lowbit(13) = 1

i -= i & -i 等价于 i &= i - 1

(-i) == ((~i)+1)
(~i) == ((-i)-1)
(-~i) == i + 1
---~i = i + 3
(~-i) == i - 1
--~-i = i - 3

补码存负数
00000011 3
00000010 2
00000001 1
00000000 0
11111111 -1
11111110 -2

lowbit(i) = i的约数中,最大的2的次幂
i写成二进制,最低是1的一位

lowbit(12) = 4
1 1 0 0
^
8 4 2 1

lowbit(17) = 1
1 0 0 0 1
16 8 4 2 1
^

质数

判断质数

筛法

高级数论

Miller Robin

Polland Rho

杜教筛

Min_25 筛

矩阵乘法

简介

信息学中遇到的矩阵,可以认为都是方阵。
(在极少数情况下还是有非方阵的情况)

为了体现出矩阵乘法的效率,往往需要和快速幂结合起来使用。
因为运算结果可能很大,所以往往需要对一个数字取模

矩阵乘法

对于两个n×nn \times n的矩阵A,BA, B可以定义矩阵乘法:
Ci,j=k=1nAi,kBk,jC_{i, j} = \sum_{k = 1}^n A_{i, k} B_{k, j}
这样定义出的矩阵乘法,满足结合律,可以使用快速幂来计算矩阵乘法。

一般情况下,为了计算不越界,都会要求对某个数字取模。

for (int i = 0; i < m; i++) {
    for (int j = 0; j < m; j++) {
        for (int k = 0; k < m; k++) {
            c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % p;
        }
    }
}

如果a[][]是一个行向量而不是矩阵,可以用以下的方式常数优化。

for (int i = 0; i < m; i++) {
    for (int k = 0; k < m; k++) {
        if (a[i][k] == 0) {
            continue;
        }
        for (int j = 0; j < m; j++) {
            c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % p;
        }
    }
}

Fibonacci数

#include <bits/stdc++.h>
using namespace std;
const int m = 2;
int a[m][m], b[m][m];
int n, p = 10000;
void mul(int a[m][m], int b[m][m]) {
    // a = a * b;
    int c[m][m] = {};
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < m; j++) {
            for (int k = 0; k < m; k++) {
                c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % p;
            }
        }
    }
    memcpy(a, c, sizeof c); // 不能写sizeof a
}
int main() {
    a[0][1] = 1;
    b[0][1] = b[1][0] = b[1][1] = 1;
    cin >> n;
    for(; n > 0; n >>= 1) {
        if (n & 1) {
            mul(a, b);
        }
        mul(b, b);
    }
    cout << a[0][0] << endl;
    return 0;
}

也可以利用Fibonacci数的性质
Fn+2=Fn+1+FnF_{n + 2} = F_{n + 1} + F_{n}
F2n+1=Fn+12+Fn2F_{2n + 1} = F^2_{n + 1} + F^2_{n}
F2n=(2Fn+1Fn)Fn2F_{2n} = (2F_{n + 1} - F_n) F^2_{n}

这样就可以从(Fn,Fn+1)(F_{n}, F_{n + 1})计算出(Fn+1,Fn+2)(F_{n+1}, F_{n+2})
也可以从(Fn,Fn+1)(F_{n}, F_{n + 1})计算出(F2n,F2n+1)(F_{2n}, F_{2n+1})
然后就可以递归进行计算。

// F(n) 返回 make_pair(f[n], f[n+1])
LL F(ll n)
{
    if(n==0)
        return LL(0,1);
    else if(n&1)
    {
        LL u=F(n-1);
        return LL(u.Y,(u.X+u.Y)%p);
    }
    else
    {
        LL u=F(n/2);
        return LL((2*u.Y+p-u.X)%p*u.X%p,(u.X*u.X+u.Y*u.Y)%p);
    }
}

参考题目

poj 3070
因为结果对1000010000取模,fnfn+15000(mod15000)f_n \equiv f_{n + 15000} \pmod{15000}
所以读入nn直接对1500015000取模即可。

P1349, P1357, P1707, P1939, P1962, P2044, P2461, P3746, P4599, P4910, P5004

P2151 [SDOI2009]HH去散步
P3169 [CQOI2015]多项式
P3193 [HNOI2008]GT考试
P3597 [POI2015]WYC
P3758 [TJOI2017]可乐
P3990 [SHOI2013]超级跳马
P5392 [Cnoi2019]雪松树之约
P5517 [MtOI2019]幻想乡数学竞赛
P5678 [GZOI2017]河神

a | (b & c)

(a | b) & (a | c)

a & (b | c)

(a & b) | (a & c)

矩阵:二维数组
在计算机比赛中用到的矩阵都是 方阵
只有方阵可以做快速幂

为什么矩阵乘法可以出成题?
快速幂 && 取模

哪类题目可以用矩阵乘法
常系数线性齐次递推

为什么矩阵乘法有结合律?
核心是乘法对加法有分配率

f[n + 1] = a f[n] + b g[n] + c h[n]
g[n + 1] = r f[n] + s g[n] + t h[n]
h[n + 1] = x f[n] + y g[n] + z h[n]
转化为类似于这样递推,
多个数组,同时推,n+1结果只和n的结果有关

矩阵加减法 对应位置相加减
矩阵乘法 不是对应位置相乘

矩阵乘法 满足 结合律 (不一定满足交换律)
可以用快速幂优化
因为 取模 使得这个方法比暴力效率高很多

零矩阵 全是0的矩阵 相当于0
零矩阵 * 任意矩阵 = 零矩阵

单位矩阵 只有对角线a[i][i]=1的矩阵 相当于1
单位矩阵 * 任意矩阵 = 任意矩阵本身

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

矩阵的0次幂可以认为是单位矩阵

转置矩阵
a[i][j] = b[j][i]

1 2 3
4 5 6
7 8 9
的转置是
1 4 7
2 5 8
3 6 9

a和b互为转置矩阵

(AB)的转置矩阵 = B的转置矩阵 * A的转置矩阵
(A
B)的逆矩阵 = B的逆矩阵 * A的逆矩阵

矩阵乘法
95%的情况和快速幂一起用
5%逆矩阵

  1. 不要用结构体(除非是非常复杂的矩阵操作)
  2. 不要用单位矩阵,尽量减少矩阵乘法的次数
  3. 乘法取模尽量快的计算
  4. 稀疏矩阵乘法,可以减少乘法次数

易错点,尽量不要涉及到矩阵的负数次方

绝大多数问题都是
矩阵A 乘以 矩阵B的n次方
A * B**n

比如 n = 21
A * B * B4 * B16

绝大多数问题,A矩阵是一个行向量(只有第一行非0)
很多矩阵乘法是
行向量*矩阵 可以优化到O(n2)的矩阵乘法时间复杂度 暴力O(n3)

有时矩阵乘法是
Bt * A
矩阵*列向量 可以优化到O(n
2)的矩阵乘法时间复杂度 暴力O(n**3)

P1962 斐波那契数列
Fibonacci Number
标准定义
f[-2] ...
f[-1] ...
f[0] = 0
f[1] = 1
做法1:

f[n], f[n+1] -> f[n+1], f[n+2] = (f[n+1], f[n] + f[n+1])
f[n], f[n+1] -> f[2n], f[2n+1] =

f[2n] = f[n]f[n+1] + f[n-1]f[n] = f[n] * (f[n+1] + f[n-1]) = f[n] * (f[n+1]2-f[n])
LL F(ll n)
{
if(n==0)
return LL(0,1);
else if(n&1)
{
LL u=F(n-1);
return LL(u.Y,(u.X+u.Y)%p);
}
else
{
LL u=F(n/2);
// u.X = f[n]
// u.Y = f[n+1]
return LL((2
u.Y+p-u.X)%p
u.X%p,(u.X
u.X+u.Yu.Y)%p);
// f[2
n] = (2f[n+1]-f[n]) * f[n];
// f[2
n+1] = f[n]*f[n]+f[n+1]*f[n+1];
}
}

gcd(f[n], f[m]) = f[gcd(n, m)];

做法2:
f[i+1] = f[i] + f[i-1]
g[i] = f[i-1]

f[i+1] = f[i] + g[i]
g[i+1] = f[i]

f[i+1] f[i]
0 0

1 1
1 0

=

(f[i+1]+f[i]) f[i+1]
0 0

=
f[i+2] f[i+1]
0 0

初始知道的是
f[1] f[0]

乘以转移矩阵n次之后得到

f[n+1] f[n]


f[i+1] = a f[i] + b g[i]
g[i+1] = c f[i] + d g[i]

f[i] g[i]
0 0

a c
b d

=

(a f[i] + b g[i]) (c f[i] + d g[i])
0 0

f[i+1] g[i+1]
0 0

P1349 广义斐波那契数列

f[i+1] f[i]
0 0

p 1
q 0

=

(pf[i+1]+qf[i]) f[i+1]
0 0

=
f[i+2] f[i+1]
0 0


初始知道的是
f[2] f[1]

乘以转移矩阵n-1次之后得到

f[n+1] f[n]


f[2] f[1] -> f[n+1] f[n]

必须是 常系数线性递推 才可以 矩阵乘法优化
如果系数中出现了 i 之类的,就不能矩阵乘法优化
f[i] = i * f[i-1]
阶乘是不能优化的

P1939 【模板】矩阵加速(数列)

f[i] = f[i-1] + f[i-3]

f[i] f[i+1] f[i+2]
0    0      0
0    0      0

*

0 0 1
1 0 0
0 1 1

=

f[i+1] f[i+2] (f[i+2]+f[i])
0      0      0
0      0      0

=

f[i+1] f[i+2] f[i+3]
0      0      0
0      0      0





f[1] f[2] f[3]
*
转移矩阵(n-1)次方
=
f[n] f[n+1] f[n+2]


假设f[0]=0
f[0] f[1] f[2] // 0 1 1
*
转移矩阵n次方
=
f[n] f[n+1] f[n+2]

P1707 刷题比赛

k*k k 1
0 0 0
0 0 0

1 0 0
2 1 0
1 1 1

=

(k+1)*(k+1) (k+1) 1
0 0 0
0 0 0


a[2] a[1] b[2] b[1] c[2] c[1] w z 1 1 1

a[k+1] a[k] b[k+1] b[k] c[k+1] c[k] w**k z**k k*k k 1

*

p 1 1 0 1 0 0 0 0 0 0
q 0 0 0 0 0 0 0 0 0 0
1 0 u 1 1 0 0 0 0 0 0
0 0 v 0 0 0 0 0 0 0 0
1 0 1 0 x 1 0 0 0 0 0
0 0 0 0 y 0 0 0 0 0 0
0 0 1 0 0 0 w 0 0 0 0
0 0 0 0 1 0 0 z 0 0 0
r 0 0 0 0 0 0 0 1 0 0
t 0 0 0 1 0 0 0 2 1 0
1 0 0 0 2 0 0 0 1 1 1

=

a[k+2] a[k+1] b[k+2] b[k+1] c[k+2] c[k+1] w**(k+1) z**(k+1) (k+1)*(k+1) (k+1) 1


a[k+2] = 

P2044 [NOI2012]随机数生成器

x[n] c

a 0
1 1

=

x[n+1] c

另一个做法


x[n + 1] = a x[n] + c
a[1] = a
c[1] = c

x[n + m] = a[m] x[n] + c[m]
问a[n] c[n]怎么求

x[n + m] = a[m] x[n] + c[m]
if m % 2 == 1:
x[n + m] = a[1] * x[n + m - 1] + c[1]
x[n + m] = a[1] * (a[m - 1] * x[n] + c[m - 1]) + c[1]
x[n + m] = a[1] * a[m - 1] * x[n] + (a[1] * c[m - 1] + c[1])
a[m] = a[1] * a[m - 1]
c[m] = a[1] * c[m - 1] + c[1]
else:
x[n + m] = a[m/2] (a[m/2] x[n] + c[m/2]) + c[m/2]
x[n + m] = a[m/2] * a[m/2] * x[n] + a[m/2] * c[m/2] + c[m/2]
a[m] = a[m/2] * a[m/2]
c[m] = a[m/2] * c[m/2] + c[m/2]

x[n] = a[m] x[0] + c[m]

等比数列求和怎么做?
公式?涉及到除法,逆元,公比是1
编程中一般用快速幂/倍增

等比数列求和
// S(n, r) == 1 + r + r2 + ... + r(n-1)

int S(int n, int r)
{
if (n == 0)
{
return 0;
}
if (n % 2 == 1)
{
return 1 + S(n - 1, r) * r;
}
else
{
return (1 + r) * S(n / 2, r * r);
}
}

P2461 [SDOI2008]递归数列

s[0] = 0;
s[n] = a[1] + a[2] + .. + a[n]

s[n] a[n+1] a[n+2] a[n+3]

1 0 0 0
1 0 0 c3
0 1 0 c2
0 0 1 c1

=

s[n+1] a[n+2] a[n+3] a[n+4]

初始
s[0] a[1] a[2] a[3]
乘以n次转移矩阵,得到
s[n] ....

P4910 帕秋莉的手环

0 -> 2
1 -> 1

Lucas Numbers

n=0 2
n=1 1
n=2 3
n=3 4
n=4 7
n=5 11

f[0] = 0
f[1] = 1
f[2] = 1
f[3] = 2
f[4] = 3
f[5] = 5
f[6] = 8

l[n] = f[n+1] + f[n-1]

P1357 花园

CCPPP PCPCPCCPPP

A * B ** 19
A * B * (B ** 18)
A * B * ((B * B) ** 9)

求出

n=5 ~ n=100 的答案
假设这个题目是线性递推数列
f[i] = a[1] * f[i-1] + a[2] * f[i-2] + ... + a[t] * f[i-t]
直接解方程,去解递推的系数a[i]

找到递推系数之后,就可以使用 矩阵乘法 或 多项式取模 来解决这个问题了

P5004 专心OI - 跳房子

f[i] 最后一步落在i的方案数
f[i] = sum(j < i - M, f[j])
s[i] = f[1] + f[2] + ... + f[i]
f[i] = s[i-M-1]
s[i] = s[i-1] + f[i]
其实不需要维护f?

s[i] = s[i-1] + s[i - M - 1]

s[0] = 1 ''
s[1] = 2 o x
s[2] = 3 oo xo ox
s[3] = 5 ooo xoo oxo oox xox
s[4] = 8
s[5] = 13

i <= M; s[i] = i + 1

s[0] = 1
s[1] = 2
s[2] = 3
s[3] = 4
s[4] = 6
s[5] = 9
s[6] = 13

s[i] s[i+1] ... s[i+M]

0 0 0 0 1
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 1

s[i+1] s[i+2] ... s[i+M+1] = s[i] + s[i+M]

s[0] .....

s[n] .....

f[0] + f[1] + ... + f[n] = f[n+2] - 1

P3746 [六省联考2017]组合数问题

C(n, r) = C(n - 1, r) + C(n - 1, r - 1)

f[0][0] = 1
f[0][1] = 0
..
f[0][k-1] = 0

f[i][j] = sum(任意t, C(i, tk + j)) = sum(任意t, C(i - 1, tk + j) + C(i - 1, tk + j - 1))
sum(任意t, C(i - 1, tk + j)) + sum(C(i - 1, tk + j - 1))
f[i][j] = f[i - 1][j] + f[i - 1][(j - 1) % k]

1 0 0
1 1 0
1 2 1
2 3 3
5 5 6
11 10 11

f[0] f[1] f[2] f[3] f[4]

1 1 0 0 0
0 1 1 0 0
0 0 1 1 0
0 0 0 1 1
1 0 0 0 1

f[0]+f[4] f[0]+f[1] f[1]+f[2] f[2]+f[3] f[3]+f[4]

P4599 [HEOI2012]赵州桥

如何用矩阵乘法,计算1到n的k次方和

(1k+2k+...+(i-1)k) i0 i1 i2 i3 ... ik

...

(1k+2k+...+i**k) (i+1)**0 (i+1)**1 (i+1)**2 (i+1)**3 ... (i+1)**k

k = 4

1 0 0 0 0 0
0 1 1 1 1 1
0 0 1 2 3 4
0 0 0 1 3 6
0 0 0 0 1 4
1 0 0 0 0 1

如何用矩阵乘法,计算1到n的k次方和

(......) i0*ri i1*ri i2*ri i3*ri ... ik*ri

...

(......+ik*ri) r*(i+1)0*r(i+1) (i+1)1*r(i+1) (i+1)2*r(i+1) (i+1)3*r(i+1) ... (i+1)k*r(i+1)

k = 5

1 0 0 0 0 0
0 1r 1r 1r 1r 1r
0 0 1r 2r 3r 4r
0 0 0 1r 3r 6r
0 0 0 0 1r 4r
1 0 0 0 0 1r

bzoj 3157
bzoj 3516
等比数列 * 等差数列(一次多项式)
等比数列 * 多项式(m次多项式)
时间复杂度O(m^2)

在计算过程中只做了一次除法,比如除以n,但是n在模mod下没有逆元,怎么办?
之前对mod取模

之后对(mod*n)取模
最后直接除以n即可,应当能整除

只做了一次除法常见的情况:
FWT / Burnside

bzoj 3157: 国王奇遇记

3516: 国王奇遇记加强版

逆矩阵

f[i+1] f[i]
0 0

1 1
1 0

=

(f[i+1]+f[i]) f[i+1]
0 0


(f[i+1]+f[i]) f[i+1]
0 0

0 1
1 -1

=

f[i+1] f[i]
0 0


1 1
1 0
*
0 1
1 -1

1 0
0 1

如果A矩阵乘以B矩阵等于单位矩阵
那么A矩阵和B矩阵互为逆矩阵

1 1 1 0
1 0 0 1

做行初等变换
1.交换某两行
2.将某一行的所有元素乘上k(k \neq 0)
3.将某一行的所有元素乘上k加到另一行去

1 1 1 0
1 0 0 1

1 0 0 1
1 1 1 0

1 0 0 1
0 1 1 -1

行初等变换
交换两行
同一行乘以一个数字k
第i行减去第j行对应的位置

在要求逆的矩阵 A 后面 接一个 单位矩阵 I
对于这个 n 行 2n 列的矩阵,做行初等变换
使得前 n 列变成单位矩阵,后 n 列就是求逆的结果

高斯消元求逆矩阵
枚举第i行:
a[i][i] 可能是 0
找第j行,使得a[j][i] != 0
交换第i行和第j行(必须让a[i][i]不为0)
第i行同时乘以 a[i][i] 的逆元 (让a[i][i]变成1
将所有不是第i行的 第j行,减去若干倍的 第i行,使得a[j][i]变成0

取模加const会变快
比如 / 10

include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
ull b, m;
FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
ull reduce(ull a) {
ull q = (ull)((L(m) * a) >> 64);
ull r = a - q * b; // can be proven that 0 <= r < 2*b
return r >= b ? r - b : r;
}
}FM(2);
int main()
{
FM = FastMod(1000000007);
cout << FM.reduce(1000000000000000000) << endl;
return 0;
}

高斯消元解方程
a11x1 + a12x2 + a13x3 == b1
a21
x1 + a22x2 + a23x3 == b2
a31x1 + a32x2 + a33*x3 == b3

已知所有的a和b,求x
nn的矩阵A * n1的列向量x = n*1的列向量b

求出A矩阵的逆矩阵A^(-1)
x = A^(-1) * b

b数组直接记在a矩阵的后面一列

P6009 [USACO20JAN]Non-Decreasing Subsequences P
第一反应,区间维护a[i][j]表示区间内以i开始,以j结束的不下降子序列个数
区间支持合并,可以线段树
超时
加了一堆常数优化,发现还是超时

已有a[i][j]表示一个数组的答案,在这个数组之后加了一个x,答案会怎么变?
已有a[i][j]表示一个数组的答案,删掉了数组中最后一个数字x,答案会怎么变?

已有a[i][j]表示一个数组的答案,删掉了数组中第一个数字x,答案会怎么变?

很多计数的动态规划,是可逆的

K = 5
x = 4

大部分位置不变
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

原本1开始i(i<=4)结束的序列,现在可以变成1开始,x结束的序列

f[4]=
1 0 0 1 0
0 1 0 1 0
0 0 1 1 0
0 0 0 2 0
0 0 0 0 1

newa[1][x] = a[1][1] + a[1][2] + ... + a[1][x]
newa[2][x] = a[2][1] + a[2][2] + ... + a[2][x]
newa[3][x] = a[3][1] + a[3][2] + ... + a[3][x]

g[4]=
1 0 0 -1/2 0
0 1 0 -1/2 0
0 0 1 -1/2 0
0 0 0 1/2 0
0 0 0 0 1

f[x] 和 g[x] 互为逆矩阵,f[x]*g[x] = g[x]*f[x] = I

可以求转移矩阵 的 前缀乘积,和 逆矩阵 的 前缀乘积

inv(AB) = inv(B)*inv(A)
inv(B)*inv(A) * A * B = inv(B)*B = I
(inv(B)inv(A)) * (AB) = I

空串:E=
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0

最终(l, r)的答案 =
E * f[a[l]] * f[a[l+1]] * f[a[l+2]] * ... * f[a[r]] =
E * (g[a[l-1]] * g[a[l-2]] * ... * g[a[1]]) * (f[a[1]] * f[a[2]] * ... * f[a[l-1]] * f[a[l]] * f[a[l+1]] * f[a[l+2]] * ... * f[a[r]])
= E * q[l-1] * p[r]

p[i] = f[a[1]] * f[a[2]] * ... * f[a[i]]
q[i] = g[a[i]] * ... * g[a[2]] * g[a[1]]

sp[r][i] = p[r][i][0] + p[r][i][1] + ... + p[r][i][k-1] (第i行的和)

最终(l, r)的答案 =
q[l-1] * p[r] 第一行的和
既然只关注第一行的值,那么就只计算第一行
for (int i = 0; i < m; i++)
{
for (int j = 0; j < m; j++)
{
ans = (ans + (long long)q[l][0][i] * p[r][i][j]) % mod;
}
}

ans = 0
for (int i = 0; i < m; i++)
{
ans = (ans + (long long)q[l][0][i] * sp[r][i]) % mod;
}

总时间复杂度
预处理:O(n * k^2)
每个询问:O(k)
总复杂度:O(n * k^2 + q * k)

行列式值为0的矩阵,没有逆矩阵
如果转移矩阵的行列式值为0,那么说明有一些列是没有用的

多个初始值的矩阵乘法
可以放在初始矩阵的不同行,节约一些时间。

0 1 Fibonacci Number f[i] = f[i-1] + f[i-2]
2 1 Lucas Number l[i] = l[i-1] + l[i-2]
*
0 1
1 1

1 1
1 3


1 2
3 4


2 3
4 7


3 5
7 11

这个方法和 快速幂时,记录下矩阵的1次幂,2次幂,4次幂,8次幂……等价。
矩阵乘法最慢的部分,是将转移矩阵自己平方


P1827 [USACO3.4]美国血统 American Heritage

乘法逆元

逆元

数学中,逆元素(英语:Inverse element)推广了加法中的加法逆元和乘法中的倒数。直观地说,它是一个可以取消另一给定元素运算的元素。

加法逆元

对于一个任意数n,存在加法逆元(英语:Additive Inverse,又称相反数),其与nn的和为零(加法单位元)。nn的加法逆元表示为n-n

乘法逆元

数学上,一个数xx的倒数(reciprocal),或称乘法逆元(multiplicative inverse),是指一个与xx相乘的积为11的数。显然在实数范围内xx的乘法逆元是1x\frac{1}{x}

信息学中常用的乘法逆元是模逆元。
一整数a对同余n之模逆元是指满足以下公式的整数bb
ab1(modn).a b \equiv 1 \pmod {n}.
整数aa对模数nn之模逆元存在的充分必要条件是aann互素,若此模逆元存在,在模数nn 下的除法可以用和对应模逆元的乘法来达成,此概念和实数除法的概念相同。

如何求

快速幂

对于质数pp,考虑费马小定理ap1modp=1a^{p-1} \bmod p = 1,可以得到ii的逆元是pow(i, p - 2, p)

对于合数pp,考虑欧拉定理aφ(p)modp=1a^{\varphi(p)} \bmod p = 1,可以得到ii的逆元是pow(i, phi(p) - 1, p)
但是因为对于一般的数字计算φ(p)\varphi(p)复杂度较高,并不使用这个方法。

扩展欧几里得

因为快速幂非常好写,一般只有在模非质数,或者是卡常数的情况下使用这个算法。
解出ax+py=1ax + py = 1的一组解(x,y)(x, y)xx就是aa关于模nn的其中一个模逆元。
需要注意求解出的xx可能是负数。

O(n)O(n)11nn的乘法逆元

inv[1] = 1;
for (int i = 2; i <= n; i++) {
    inv[i] = (long long)inv[p % i] * (p - p / i) % p;
}

这个方法可以用于组合数的预处理。

这个方法还可以改写为递归版,以替代质数情况下用快速幂求逆元。

int inv(int x) {
    if (x == 1) {
        return 1;
    } else {
        return (long long)inv(p % i) * (p - p / i) % p;
    }
}

另一个常用与组合数预处理的做法是:
先预处理出11nn的阶乘,然后计算nn阶乘的逆元,然后倒序推出nn11阶乘的逆元。

fac[0] = 1;
for (int i = 1; i <= n; i++) {
    fac[i] = (long long)fac[i - 1] * i % p;
}
invfac[n] = pow(fac[n], p - 2, p);
for (int i = n - 1; i >= 0; i--) {
    invfac[i] = (long long)invfac[i + 1] * (i + 1) % p;
}

参考题目

Luogu P1082

参考资料

贾志鹏线性筛

复数

实数 虚数

bitset

如果我们只需要 位与 位或 位异或,那么直接自己压位也很简单
If we need xor or and, it easy to implement ours.

但是如果我们需要 左移 右移,自己实现就很麻烦
If we need shift left and shift right, it hard to implement ours.

3个用法

  1. Floyd
  2. bitset优化(零一)背包
  3. bitset解方程
  4. ....其他题目,比如 CF878D

P4306 [JSOI2010]连通数
Floyd O(n^3) bitset 优化
暴力BFS O(nm)

不存在O(n + m)的做法

for (int k = 0; k < n; k++)
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			d[i][j] |= d[i][k] && d[k][j];
		}
	}
}


for (int k = 0; k < n; k++)
{
	for (int i = 0; i < n; i++)
	{
		if (d[i][k])
		{
			for (int j = 0; j < n; j++)
			{
				d[i][j] |=  d[k][j];
				// 第i行 |= 第k行
			}
		}
	}
}

for (int k = 0; k < n; k++) {
	for (int i = 0; i < n; i++) {
		if (d[i][k]) {
			d[i] |= d[k];
		}
	}
}

spoj ADACOINS

直接背包
f |= f << x;

atcoder agc020_c
注意到子集和都是成对的,
x, s-x
唯一不成对的是s,因为没有0
所以等价于求大于等于s/2最小的子集和是什么

bzoj 3697
http://hzwer.com/3697.html
哪里出错了?

bitset优化解方程:
spoj
JZPLIT
JZPLIT2

spoj ADAFUROW

bitset 优化集合暴力

https://www.spoj.com/problems/ADAFUROW/

spoj ADACHERY

https://www.spoj.com/problems/ADACHERY/

三角函数

sin

cos

tan

正弦定理

余弦定理

sin() = 对边 除以 斜边
cos() = 底边 除以 斜边
tan() = 对边 除以 底边
对边 ** 2 + 底边 ** 2 == 斜边 ** 2

tan(x) = sin(x) / cos(x)

sin(0) = 0
cos(0) = 1
tan(0) = 0

sin(直角) = 1
cos(直角) = 0
tan(直角) = 无意义

程序中的 sin 和 cos 参数都是弧度
角度,圆周是360度,直角是90度
弧度,圆周是2pi,直角pi/2

周角 = 360度 = 2pi
平角 = 180度 = pi
直角 = 90度 = pi/2

角度 / 180 * pi = 弧度
弧度 / pi * 180 = 角度

扇形的弧长,就是弧度乘以半径

求 sin 30度,应该写
sin(30 / 180 * pi) = 0.5
特殊角的三角函数值

sin 和 cos 的参数可以是任意角(不一定是锐角)
cos(平角) = cos(pi) = -1

如果一个点和原点的距离是 r
从x轴正方向,逆时针转 a 弧度 转到这个点的方向
那么这个点的坐标是
r * cos a, r * sin a

H时M分,时针 的角度是多少?

hypot 输入直角三角形的两个直角边的长度,返回斜边的长度
hypot(x, y) = sqrt(x * x + y * y)

三角形三角形的3条边长度是 a, b, c
长度为a的边 对应的角 是A
长度为b的边 对应的角 是B
长度为c的边 对应的角 是C

勾股定理
当 C 是直角时
a * a + b * b == c * c

余弦定理
当 C 是任意角时
a * a + b * b - 2 * a * b cos(C) == c * c

已知三边长度,求三个角度,可以用 余弦定理 (和arcos)来求

LGV 引理

P6657 【模板】LGV 引理

卢卡斯定理

简介

对于非负整数m和n和素数p, 同余式:

(mn)i=0k(mini)(modp),{\displaystyle {\binom {m}{n}}\equiv \prod _{i=0}^{k}{\binom {m_{i}}{n_{i}}}{\pmod {p}},}
成立。其中:
m=mkpk+mk1pk1++m1p+m0,{\displaystyle m=m_{k}p^{k}+m_{k-1}p^{k-1}+\cdots +m_{1}p+m_{0},}
并且
n=nkpk+nk1pk1++n1p+n0{\displaystyle n=n_{k}p^{k}+n_{k-1}p^{k-1}+\cdots +n_{1}p+n_{0}}
mmnnpp进制展开。当m<nm < n时,二项式系数(mn)=0{\displaystyle {\tbinom {m}{n}}=0}

代码

int C(int n, int m) {
    if (m > n || m < 0) {
        return 0;
    }
    m = min(m, n - m);
    if (n < p && m < p) {
        int re = 1;
        for (int i = 0; i < m; i++) {
            re = (long long)re * (n - i) % p * pow(i + 1, p - 2, p) % p;
        }
        // 或改为预处理阶乘和阶乘逆元的版本
        return re;
    }
    return (long long)C(n % p, m % p) * C(n / p, m / p) % p;
}

奇偶性

当只需要判断组合数奇偶性时,可以通过位运算来判断。

证明

勒让德定理

在正数n!n!的质因子标准分解式中,质数pp的指数是
k>=1npk\sum_{k>=1} \left\lfloor \frac{n}{p^k} \right\rfloor

库默尔定理

库默尔定理指出,给定整数nm0n \geq m \geq 0和一个质数pp(nm)\binom{n}{m}pp的次数是在pp进制下mmnmn-m的进位次数。

参考题目

bzoj 1902

Luogu P1869

参考资料

https://en.wikipedia.org/wiki/Lucas's_theorem

https://en.wikipedia.org/wiki/Kummer's_theorem

叉积

二维情况

(x1, y1) * (x2, y2) = x1 * y2 - x2 * y1

也可以用行列式来理解

x1 y1
x2 y2

二阶行列式和三阶行列式有对角线法则,所以值是x1 y2 - x2 y1

二维叉积的含义

顶点是 (0, 0) (x1, y1) (x2, y2) (x1+x2, y1+y2) 平行四边形的面积

更常用的是 (0, 0) (x1, y1) (x2, y2) 三角形面积的两倍

叉积不满足交换律

(x1, y1) * (x2, y2) = -(x2, y2) * (x1, y1)

如果从 (x1, y1) 转到 (x2, y2) 逆时针是正,顺时针是负,共线是0。

叉积也可以用来是否三点共线

叉积可以用来判断点在线段的哪一侧

二维情况下,两个向量的叉积是一个数字

三维情况

立体几何

三维情况下,两个向量的叉积是一个向量

(x1, y1, z1) * (x2, y2, z2) = 
(
y1 * z2 - y2 * z1,
z1 * x2 - z2 * x1,
x1 * y2 - x2 * y1
)

求平面法向量

顶点是 (0, 0, 0) (x1, y1, z1) (x2, y2, z2) (x3, y3, z3) .... 平行六面体的体积
是行列式

x1 y1 z1
x2 y2 z2
x3 y3 z3

的值,是x1 y2 z3 + x2 y3 z1 + x3 y1 z2 - z1 y2 z3 - z2 y3 x1 - z3 y1 x2

混合积

行列式

多边形面积

多边形每个点向原点连线

每条边对应一个三角形

三角形的面积可正可负(取决于两个端点之间是顺时针还是逆时针)

求所有三角形的有向面积之和,就是多边形的面积

匹克定理

Convex Hull

Cross Product

(x1, y1) x (x2, y2) = x1y2 - x2y1

use cross product to judge direction

Convex Hull

  1. sort the points in horizontal order
    use Monotonic Stack and Cross Product scan twice
    No precision Error
    https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P2742.cpp

  2. sort the points in argular order
    choose the bottom left one, calculate the angle
    use Monotonic Stack and Cross Product scan one

We can do binary search on the convex hull

Dynamic Covex Hull

Never seen in USACO

use two sets to maintain the points of upper/lower convex hull

https://codeforces.com/problemsets/acmsguru/problem/99999/277

Volume of a Parallelepiped

图论

图论(英语:Graph theory)是组合数学的一个分支。图是图论的主要研究对象。图是由若干给定的顶点及连接两顶点的边所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物间具有这种关系。

图论起源于著名的柯尼斯堡七桥问题。该问题于1736年被欧拉解决,因此普遍认为欧拉是图论的创始人。

基本概念

图(Graph)用于表示物件与物件之间的关系,是图论的基本研究对象。一张图由一些小圆点(称为顶点或结点)和连结这些圆点的直线或曲线(称为边)组成。

二元组的定义

一张图 GG 是一个二元组 (V,E)(V, E),其中 VV 称为顶点集, EE 称为边集。它们亦可写成 V(G)V(G)E(G)E(G)EE的元素是一个二元组数对,用 (x,y)(x, y) 表示,其中 x,yVx, y \in V

有向图和无向图

如果给图的每条边规定一个方向,那么得到的图称为有向图,其边也称为有向边。在有向图中,与一个节点相关联的边有出边和入边之分,而与一个有向边关联的两个点也有始点和终点之分。相反,边没有方向的图称为无向图。

简单图

一个图如果

  1. 没有重边。即没有两条边,它们所关联的两个点都相同(在有向图中,没有两条边的起点终点都分别相同);
  2. 没有自环。即每条边所关联的是两个不同的顶点。
    则称为简单图(Simple graph)。

度(Degree):一个顶点的度是指与该顶点相关联的总边数,顶点 vv 的度记作 d(v)d(v)。度和边有如下关系:
vVd(v)=2E\sum_{v \in V} d(v) = 2|E|

出度和入度

出度(Out-degree)和入度(In-degree):对有向图而言,顶点的度还可分为出度和入度。一个顶点的出度为 dod_o,是指有 dod_o 条边以该顶点为起点,或说与该点关联的出边共有 dod_o 条。入度的概念也类似。

子图

子图(Sub-Graph):图 HH 称作图 GG 的子图如果 V(H)V(G)V(H) \subseteq V(G) 以及 E(H)E(G)E(H) \subseteq E(G)

如果图 GG 的子图 HH,满足V(H)=V(G)V(H) = V(G),即图 HH 包含图 GG 的所有顶点,则称HHGG的生成子图。特别的如果子图 HH 是树,则称子图 HH 为生成树。

如果图 GG 的子图 HH,满足对于u,vV(H)u, v \in V(H),边 (u,v)(u, v) 在图HH中当且仅当边(u,v)(u,v)在图GG中,即图 HH 包含了图 GG 中所有两个端点都在 V(H)V(H) 中的边,则称HHGG的导出子图。

补图

一个图 GG 的补图(complement)是一个图有着跟 GG 相同的点,而且这些点之间有边相连当且仅当在 GG 里面他们没有边相连。

完全图

完全图是所有顶点两两相邻的图。nn阶完全图,记作KnK_nnn阶完全图有n(n1)/2n(n-1)/2条边。

独立集

独立集(independent set)是图论中的概念。一个独立集是一个图中一些两两不相邻的顶点的集合。换句话说它是一个由顶点组成的集合 SS,使得 SS 中任两个顶点之间没有边。等价地,图中的每条边至多有一个端点属于SS

二分图

二分图指顶点可以分成两个不相交的集 UUVV,使得在同一个集内的顶点不相邻(没有共同边)的图,即UUVV皆为独立集。

完全二分图

完全二分图是一种特殊的二分图,可以把图中的顶点分成两个集合X,YX, Y,使得集合XX中的所有顶点都与集合YY中的所有顶点相连。若 X=m,Y=n|X| = m, |Y| = n 则图 GG 计作 Km,nK_{m, n}

平面图

平面图是可以画在平面上并且使得不同的边可以互不交叠的图。而如果一个图无论怎样都无法画在平面上,并使得不同的边互不交叠,那么这样的图不是平面图,或者称为非平面图。完全图K5和完全二分图K3,3(汤玛森图)是最“小”的非平面图。

NP完全

图论中有很多经典的NP完全问题

  1. 子图同构。输入图 GG 和图 HH 问是否存在 GG 的子图与 HH 同构。
  2. 哈密顿路径问题。输入无向图 GG 问是否存在经过每个点恰好一次的路径。
  3. 旅行推销员问题。输入无向图 GG 有边权,希望经过每个点恰好一次,并且权值和最小。
  4. 最大团。输入无向图 GG 求最大团。
  5. 最大独立集。输入无向图 GG 求最大独立集。
  6. 图着色问题。输入无向图 GG,每个点染一个颜色,希望相邻的点颜色不同,问最小色数。

如果在做题中遇到类似问题,说明

  1. 转化中忽略了条件,比如最大独立集,一般图做不了,但是二分图可以做。
  2. 忽略了数据范围,旅行推销员问题是经典的状态压缩DP。
  3. 考查近似算法。很多NP完全问题,有效果不错的近似算法。

图的存储

参考单独页面 图的存储

图的遍历

在竞赛中,经常需要计算出有多少个连通块和每个连通块的大小。
主要有以下三个方法

  1. 并查集
  2. 深度优先搜索(DFS)
  3. 广度优先搜索(BFS)

拉姆齐定理

要找这样一个最小的数 R(k,l)=nR(k, l) = n,使得 nn 个人中必定有 kk 个人相识或 ll 个人互不相识。

一个鸽笼原理的简单应用是证明R(3,3)=6R(3, 3) = 6

欧拉回路

见欧拉回路

参考题目

参考资料

图 (数学)

拉姆齐定理

图论 Graph Theory

存边

  1. 直接存
  2. 邻接链表 / vector
  3. 邻接矩阵 Prim / Floyd

有向边
无向边

  1. 直接存
    (起点, 终点, 边权)
    方便按边权排序,Kruskal

按起点排序(这一步可以计数排序)
记录每个点的第一条边和最后一条边是谁
通过循环可以得到起点相同的所有边

比如初始有
1 2
2 3
3 1
三条边

1 2
2 1
2 3
3 2
3 1
1 3

排序得到
1 2 // 0
1 3 // 1
2 1 // 2
2 3 // 3
3 1 // 4
3 2 // 5

first[1] = 0 从1出发的第一条边,下标是0
first[2] = 2
first[3] = 4
first[4] = 6

如果想访问从x出发的所有边
for (int i = first[x]; i < first[x + 1]; i++)
{

}
就可以了

邻接链表 adjacent list

使用vector

  1. adjacent list or vector(ArrayList)

int head[];
struct Edge
{
int next; // use int to represent the index rather than pointer.
int to;
int weight;/
}

enumerate all edges from x

for (int i = head[x]; i != 0; i = a[i].next)
{
	if (a[i].to   a[i].weight)
	{

    }

}

vector<pair<int, int> > a[1000020];

from x to y length z
a[x].push_back(make_pair(y, z));

需求
给定起点x,找到起点x出发的所有的边
主要矛盾:一个点的出边,可能很多(n个)可能很少(1个)
邻接链表

优点比较快
缺点比较麻烦
只能访问全部,不方便删除或者排序

邻接链表,后加入的会先被访问到

每个点x维护 head[x] 指向这个链表的第一个边

每个边除了维护自身的信息比如 to[i] length[i] 再维护一个 next[i] 表示链表
有的人会用结构体把这3个变量存在一起,这个不涉及到结构体做参数和返回值,所以用不用都可以
有的人会用指针实现链表存边
对于做比赛来说,没必要用指针,凡是需要用 pointer 的地方,都可以记录数组下标
建议不要用指针,因为很难调试
对于做比赛来说,一般不用 new 和 delete

如果存双向边,需要开双倍数组

for (int i = head[x]; i; i = next[i]) // 如果 i==0 认为没有边了,有的人会用 -1 表示没有边
{
有一条边从 x 到 to[i] 边长是 length[i]
如果i是奇数
那么i的反向边是i+1
如果i是偶数
那么i的反向边是i-1
如果不知道x,如何确定第i条边是从哪里出发的
看反向边to是多少
}

如果 head[x] 是 0,那么说明没有从 x 出发的边

head[x] 是第一条边
next[head[x]] (如果有) 是第二条边
next[next[head[x]]] (如果有) 是第三条边

void addEdge(int x, int y, int z) // x 到 y 边长是 z
{
edgecnt++;
next[edgecnt] = head[x]
head[x] = edgecnt;
length[edgecnt] = z;
}

有的人会利用
如果我们加双向边
那么 1 和 2 互为反向边,3 和 4 互为反向边
用链表可以很容易的找到反向边
这个在网络流中有用
一般为了方便
都会从2开始存
让 2和3一组 4和5一组
这样的话i的反向边是 i^1

vector<pair<int, int> > a[100020];
(终点, 权值)
优点好写,可以完成排序等操作
缺点:慢

一般只要输入的是
(起点, 终点, 边长)
都是这样存,可以避免重边

vector会预留16个位置,
用完之后,倍增,大小乘以2
这样平均下来是O(1)

邻接矩阵 adjacent matrix

it depends on the input.
(x, y, z1)
(x, y, z2)

from x to y length z
a[y][x] = z

点数n
a[i][j]表示i到j的边的情况
需要n*n空间
如果点数很大,一般是开不了的。

如果输入不是n*n的数组,非常不建议用这个方法
因为有很多问题
比如输入有重边怎么办?

只用Floyd用邻接矩阵

最短路

特殊的最短路:

  1. 边权为1的最短路,可以用BFS解决
  2. 有向无环图的最短路,可以用拓扑排序解决

4个算法

  1. Bellman-Ford 基本没用
  2. Dijkstra 堆优化的版本非常常见,几乎所有最短路都是
  3. Floyd 任意两点之间的最短路,可以用来 求最小环 传递闭包
  4. SPFA 中国人发明的算法,外国人基本不知道。队列优化版的BF,可以求负环。
    如何求最短路的方案?
    最短路树,最短路图

有向图求
所有点到某一点的最短路
等价于
某一点到所有点的最短路
只需要翻转图中所有边

差分约束

Bellman-Ford
一个起点,到所有点的最短路

d[i] 从起点到 i 的最短路的长度

初始化
d[起点] = 0
d[非起点] = 正无穷大

松弛操作

从 x 到 y 长度是 z 的边
if (d[y] > d[x] + z)
{
d[y] = d[x] + z;
}

for (int i = 0; i < 点数-1; i++) // 最坏情况,需要n-1轮
{
for (枚举所有的边(任意顺序) (x, y, z))
{
对(x, y, z)做松弛操作
}
}

最终求出最短路后,对于任意边(x, y, z)一定满足
d[y] <= d[x] + z

可以用队列优化获得 SPFA
可以用优先队列优化获得 Dijkstra

Dijkstra
要求所有边都是非负的
可以 Prim 对比
需要用到 priority_queue 或者 set 保证每次取到距离最小的点
需要注意 priority_queue 默认大的在前
priority_queue 不支持删除任意点,需要特殊处理
每一个点恰好出队1次
每一条边恰好被枚举到1次
时间复杂度 (n + m) log n

Dijkstra 可以堆优化, Prim 也可以堆优化
Prim 堆优化不如 Kruskal
暴力的Prim 在完全图上时间复杂度是 O(n^2) 比堆优化的要快
Dijkstra 在完全图上 暴力比堆优化快

假设图中所有边权都是1,不需要使用优先队列,使用队列就足够了。

while (q.size() > 0) {
pair<int, int> u = q.top();
q.pop(); // 暴力 O(n) 堆优化 O(log n)
if (-u.first > d[u.second]) {
continue;
}
for (int i = 0; i < a[u.second].size(); i++) {
pair<int, int> e = a[u.second][i];
if (d[e.first] > d[u.second] + e.second) {
d[e.first] = d[u.second] + e.second;
q.push(make_pair(-d[e.first], e.first));
// 暴力 O(1) 堆优化 O(log n)
}
}
}

这种 Dijkstra 不是原版的 Dijkstra,是有区别的
为了在算法比赛中比较容易实现做出了一些改进
区别是省略 一个标记数组

如果有负边权会怎样?
如果限制每个点只进队1次,会出错
如果不限制每个点只进队1次,会超时,时间复杂度变成指数

P1608 路径统计
两个点之间有多条长度相同的边,计算最短路方案数时只算1次
所以不能加入起点终点长度均一样的边,会导致出错。

方案数可能非常大,但是题目没有考虑这种情况。

P2384 最短路
取对数避免越界
可能会有精度问题

2939 [USACO09FEB]Revamping Trails G
分层图

Floyd
无向边看做两条有向边

初始的距离d[x][y] x到y的距离
我们定义一个数组 f[k][x][y] ,
表示只允许经过(不算起点和终点)结点1到k ,结点x到结点y的最短路长度。

f[0][x][y] = d[x][y] (不能有中间节点
f[n][x][y] 就是我们需要的答案

f[k][x][y] = min(f[k-1][x][k] + f[k-1][k][y], f[k-1][x][y])

for (k = 1; k <= n; k++) { // 枚举中间点
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}

时间复杂度 O(n^3)

P2910 [USACO08OPEN]Clear And Present Danger S
直接做就可以了

  1. 无向图最小环
    for (k = 1; k <= n; k++) {
    for (i = 1; i <= n; i++) {
    for (j = 1; j <= n; j++) {
    f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    }
    }
    for (int x = 1; x <= n; x++)
    {
    for (int y = 1; y <= n; y++)
    {
    最小环 = min(最小环, a[k][x] + a[k][y] + d[x][y]);
    // 记录一下原图是什么
    }
    }
    }
    hdu 1599

  2. 传递闭包,bitset优化
    对一个图DFS/BFS的时间复杂度O(n + m)
    P4306 [JSOI2010]连通数
    枚举起点
    n * O(n + m)

f[i][j] == 1 表示 i 可以到 j
f[i][j] == 0 表示 i 不可以到 j

for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (f[i][k] && f[k][j])
{
f[i][j] = 1;
}
}
}
}

for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
if (f[i][k])
{
for (int j = 0; j < n; j++)
{
if (f[k][j])
{
f[i][j] = 1;
}
}
}
}
}

for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
if (f[i][k])
{
for (int j = 0; j < n; j++)
{
f[i][j] |= f[k][j];
}
}
}
}

bitset<2000> f[2000];
for (int k = 0; k < n; k++)
{
for (int i = 0; i < n; i++)
{
if (f[i][k])
{
f[i] |= f[k];
}
}
}

n^3

操作32位,
bool a[32], b[32];
unsigned a, b;
bitset<32> a, b;
for (int i = 0; i < 32; i++)
{
c[i] = a[i] | b[i];
}

c = a | b;
c = a | b;

bitset 3个用法

  1. 传递闭包
  2. 背包
  3. 高斯消元解异或方程

P2052 [NOI2011]道路修建
当时NOI现场,DFS是无法通过的,因为会爆栈,1000000层太多
需要long long

DFS一个树,经常需要的
没有根的,定一个根,选择1和n

void dfs(int x, int x的父亲节点)
{
for (枚举x的所有邻居i)
{
if (i != x的父亲节点)
{
dfs(i);
}
}
}

为了避免爆栈,使用BFS

P1514 引水入城

P1529 [USACO2.4]回家 Bessie Come Home

P1656 炸铁路

P1828 [USACO3.2]香甜的黄油 Sweet Butter

P3478 [POI2008]STA-Station
深度之和最大
求出每个点的深度之和
2次DFS的做法

第一次DFS,求出每个子树的答案 (子树个根节点就是根节点,其他点到子树的根节点的距离)
根节点的答案,就是最终的答案了,其他节点的答案,没有考虑父亲节点方向的节点
第二次DFS,把根节点的答案传到所有节点

SP7363 TREESUM - Tree Sum
一个集合A
s0 = A集合中所有数字0次方的和
s1 = A集合中所有数字1次方的和
s2 = A集合中所有数字2次方的和

s0' = A集合中所有数字加一之后0次方的和
s1' = A集合中所有数字加一之后1次方的和
s2' = A集合中所有数字加一之后2次方的和

s0' = s0
s1' = s1 + s0
s2' = s2 + 2 s1 + s0
s3' = s3 + 3 s2 + 3 s1 + s0

P4827 [国家集训队] Crash 的文明世界
树形DP 两次DFS

如果一个题目,求每个点的xxx的答案,应该是2次DFS

深度之和最小
重心:删除重心之后,树变成了若干个更小的树,其中最大的最小。
删去重心之后,任意一个子树大小都<=n/2
特殊情况,一个树可以有2个重心。

P2845 [USACO15DEC]Switching on the Lights S

P3916 图的遍历
反向建图,先处理答案为n的,处理答案为n-1。。。。

P5318 【深基18.例3】查找文献
需要把每个点的出边排序

树 DFS序列
进出栈序
1 2 3 3 4 4 2 5 5 1
总长度2n,每个点恰好出现2次

同一个子树的点一定是连续的一段

输入2个点x和y判断x和y的关系

  1. x == y
  2. x是y的祖先
  3. y是x的祖先
  4. x和y没关系

区间的包含关系 等价于 图中的祖先关系

1 2 3 3 4 4 2 5 5 1

如果吧
第一次出现看做+
第二次出现看做-
那么 根节点到某个节点的一条链,是DFS序的一个前缀

欧拉序
1 2 3 2 4 2 1 5 1
最后一个1一般不记录
长度是2n-2
每个点恰好出现度数次

可以用来求LCA,两个点之间深度最浅的点,就是LCA

CF1328E
对于每个询问
第一步对于输入的k个节点,每个节点变为自己的父亲节点
第二步,求出这些节点中 最靠右的左端点 最靠左的右端点
最靠右的左端点 < 最靠左的右端点 YES
最靠右的左端点 > 最靠左的右端点 NO

P1514 引水入城
无解
假设第一行都建了水站,问最后一行哪些位置无法被覆盖
如果不是无解,那么每个点建水站,能覆盖的区间一定是连续一段,
只需要考虑每个水站覆盖的最靠左的位置,最靠右的位置,用动态规划
贪心即可

如何生成一个合法的解
按退栈顺序

P1333 瑞瑞的木棍
g[s] = g.size();
g[s] 是 s 加入之前的大小,还是加入之后的大小?跟编译器有关

s[4]
((s[0] * base + s[1]) * base + s[2]) * base + s[3]
((s[0] * base ^ s[1]) * base ^ s[2]) * base ^ s[3]

P1341 无序字母对

  1. 路还是回路
  2. 有向还是无向

for (int i = 1; i <= n; i++) {
if (d[i] & 1) {
if (start == -1) {
start = i;
}
cnt++;
}
}

for (int i = n; i > 0; i--) {
if (d[i] & 1) {
start = i;
cnt++;
}
}

生成方案
按退栈顺序倒着生成

字典序最小的欧拉回路
每次优先DFS标号小的点

最短路 Shortest Path

4 个算法
4 Algorithms

  1. Bellman Ford
  2. Dijkstra
  3. Floyd
  4. SPFA

https://www.luogu.com.cn/problem/P2176

P1339 [USACO09OCT]Heat Wave G
P5837 [USACO19DEC]Milk Pumping G
P3659 [USACO17FEB]Why Did the Cow Cross the Road I G
P6145 [USACO20FEB]Timeline G
P1339 [USACO09OCT]Heat Wave G
P1821 [USACO07FEB]Silver Cow Party S
P2865 [USACO06NOV]Roadblocks G

CF938D Buy a Ticket
CF666B World Tour
CF986A Fair

图的存储

前向星链表 或 vector
参考单独页面 图的存储

基本性质

对于无向图来说,从点S到点T的最短路长度 一定等于 从点T到点S的最短路长度
对于有向图来说,从点S到点T的最短路长度 一定等于 所有边反向之后从点T到点S的最短路长度

做一次Dijkstra 可以求出起点到所有点的最短路
如果需要求所有点到某个终点的最短路,可以把边反向,把终点当做起点,再求最短路

如果S到T的最短路是 S -> A -> B -> ... -> C -> D -> T
那么对于中间的任何一个点,路径的前缀就是到他的最短路
不可能发生S到T是最短路,但是S到最短路上某个点,有更短的方案

Bellman Ford

对于一条从uuvv长度为ww的边,最终的最短路一定满足dvdu+wd_v \leq d_u + w否则dvd_v还可以被更新为du=wd_u = w,这也叫做松弛操作。

循环nn次,对每条边进行松弛操作,便可以得到每个点的最短路。

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        d[v[i]] = min(d[v[i]], d[u[i]] + w[i]);
    }
}

特别的,如果第nn轮更新(即i=n1i = n - 1时)发生了松弛操作,那么说明某个点的最短路上存在nn条边,这是不可能的,一定有负环的存在。

Bellman Ford

https://en.wikipedia.org/wiki/Bellman–Ford_algorithm

Time O(nm)
时间复杂度 O(nm)

Relax Operation
松弛操作

if (dist[edge_end] > dist[edge_start] + edge_length)
{
    dist[edge_end] = dist[edge_start] + edge_length;
}

如果一条边终点的距离 大于 起点的距离 加 边的长度
把终点的距离 改成 起点的距离 加 边的长度

n is the number of vertices.
如果 n 是图的点数

There are at most n-1 edges on the shortest path.
那么最短路上至多有 n-1 条边

如果最短路上有n条边
就意味着某个点到过2次
就意味着出环了(长度为0的环或者长度为负数的环)

for (int i = 0; i < n; i++) // n-1 times 循环 n-1 次
{
    bool changed = false;
    for (int j = 0; j < m; j++) // all edges 枚举所有的边
    {
        // Relax Operation 松弛操作
        if (dist[end[j]] > dist[start[j]] + length[j])
        {
            dist[end[j]] = dist[start[j]] + length[j];
            changed = true;
        }
    }
    // 如果枚举所有的边,没有进行松弛操作,那么退出
    if (!changed)
    {
        break;
    }
}

P1938 [USACO09NOV]Job Hunt S

d[i] largest income ending at vertex i.

if (dist[edge_end] < dist[edge_start] + edge_length)
{
    dist[edge_end] = dist[edge_start] + edge_length;
}

如果最短路上有n条边,那么这个图中有负环
If there are n edges on the shortest path, then there is a negative-weight cycle.
USACO Heat Wave

Dijkstra

基于贪心的思想,不能处理负权的情况,每个点额外维护一个属性,表示是否确定了最短路。

每次找到距离最短,不确定最短路的点,标记为确定了最短路,用于更新其他节点。

d[s] = 0;
for (int i = 0; i < n; i++) {
    int t = -1;
    for (int j = 0; j < n; j++) {
        if (!v[j]) {
            if (t == -1 || d[j] < d[t]) {
                t = j;
            }
        }
    }
    v[t] = 1;
    for (each edge e of t) {
        relax(e);
    }
}

这个算法可以被堆或者线段树优化,理论最优的做法是用Fibonacci堆,但是效果一般。
但是在实际实现中,往往不标记,实现成优先队列优化的SPFA。

Dijkstra

用来求最短路
Dijkstra can not deal with negative edges.
Dijkstra 不能处理负权最短路

use set or priority_queue to optimize Bellman Ford
相当于用 set 或者 priority_queue 来优化 Bellman Ford

The algorithm we used, is not exactly the same as the algorithm in the paper.
这个算法实际实现出来,并不和论文上的算法一模一样

priority_queue, we can only delete the top
priority_queue 只能删除最大值

set, we can delete anything we want
set 可以删除任意值(相当于支持修改)

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4779.cpp

in C++
by default, priority_queue top is the largest one.
默认,C++中的 priority_queue 的 top 是最大的

Traditional Version
论文上的实现方法

for (int i = 0; i < n; i++) // 循环n次
{
// find a unvisited vectex with the minimum distance
找到一个 距离最小 未标记的点

标记上他

更新所有他能到的点的距离
update all its neighbors

}

时间复杂度 O(m log n)
Time: O(m log n)

#include <bits/stdc++.h>
using namespace std;
int n, m, s;
vector<pair<int, int> > a[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
    scanf("%d%d%d", &n, &m, &s);
    for (int i = 0; i < m; i++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        a[x].push_back(make_pair(y, z));
    }
    memset(d, 0x3f, sizeof d); // d[i] = 0x3f3f3f3f;
    // 
    d[s] = 0;
    q.push(make_pair(-d[s], s)); // q.top() is the largest
    while (q.size() > 0) {
        pair<int, int> u = q.top();
        q.pop();
        if (-u.first != d[u.second]) {
        // if u is not the newest
        // what happens if do not continue
            continue;
        }
        // if there is no negative edges.
        // for every u.second, we do it once.
        for (int i = 0; i < a[u.second].size(); i++) {
            pair<int, int> e = a[u.second][i]; // in total m times.
            if (d[e.first] > d[u.second] + e.second) {
                d[e.first] = d[u.second] + e.second;
                q.push(make_pair(-d[e.first], e.first)); // log n
                // push the new version
                // do not delete the old version
                // priority queue does not support deletion.
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        printf("%d%c", d[i], i == n ? '\n' : ' ');
    }
    return 0;
}

参考题目

spoj ADRABR

P5201 [USACO19JAN]Shortcut G

https://www.luogu.com.cn/problem/P5201

题目背景

USACO 19年一月月赛金组第三题

题目描述

每天晚上,Farmer John都会敲响一个巨大的铃铛,召唤他的奶牛们前来牛棚享用晚餐。奶牛们都急切地想要前往牛棚,所以她们都会沿着最短的路径行走。
农场可以描述为N块草地(1≤N≤10,000),方便起见编号为1…N,牛棚位于草地1。草地之间由M条双向的小路连接(N-1≤M≤50,000)。每条小路有其通过时间,从每块草地出发都存在一条由一些小路组成的路径可以到达牛棚。

草地i中有ci头奶牛。当她们听到晚餐铃时,这些奶牛都沿着一条消耗最少时间的路径前往牛棚。如果有多条路径并列消耗时间最少,奶牛们会选择其中“字典序”最小的路径(也就是说,她们通过在两条路径第一次出现分支的位置优先选择经过编号较小的草地的方式来打破并列关系,所以经过草地7、3、6、1的路径会优先于经过7、5、1的路径,如果它们所消耗的时间相同)。

Farmer John担心牛棚距离某些草地太远。他计算了每头奶牛路上的时间,将所有奶牛消耗的时间相加,称这个和为总移动时间。他想要通过额外添加一条从牛棚(草地1)连接到某块他选择的其他草地的、通过时间为T(1≤T≤10,000)的“近道”来尽可能地减少总移动时间。当一头奶牛在她平时前往牛棚的路上偶然看见这条近道时,如果这条近道能够使她更快地到达牛棚,她就会走这条路。否则,一头奶牛会仍然沿着原来的路径行走,即使使用这条近道可能会减少她的移动时间。

请帮助Farmer John求出通过添加一条近道能够达到的总移动时间减少量的最大值。

输入格式

输入的第一行包含N、M和T。以下N行包含c1…cN,均为0…10,000范围内的整数。以下M行,每行由三个整数a,b和t描述了一条小路,这条小路连接了草地a和b,通过时间为t。所有的通过时间在1…25,000范围内。

输出格式

输出Farmer John可以达到的总移动时间的最大减少量。

样例 #1

样例输入 #1
5 6 2
1 2 3 4 5
1 2 5
1 3 3
2 4 3
3 4 5
4 5 2
3 5 7
样例输出 #1
40

提示

参考代码

#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int> > a[10020];
int n, m, t;
int c[10020];
int d[10020];
int f[10020];
set<pair<int, int> > s;
vector<int> q;
int main() {
	scanf("%d%d%d", &n, &m, &t);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &c[i]);
	}
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		a[y].push_back(make_pair(x, z));
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	s.insert(make_pair(d[1], 1));
	while (s.size()) {
		int u = s.begin() -> second;
		q.push_back(u);
		s.erase(s.begin());
		for (int i = 0; i < a[u].size(); i++) {
			if (d[a[u][i].first] > d[u] + a[u][i].second) {
				s.erase(make_pair(d[a[u][i].first], a[u][i].first));
				d[a[u][i].first] = d[u] + a[u][i].second;
				f[a[u][i].first] = u;
				s.insert(make_pair(d[a[u][i].first], a[u][i].first));
			} else if (d[a[u][i].first] == d[u] + a[u][i].second) {
				f[a[u][i].first] = min(f[a[u][i].first], u);
			}
		}
	}
	long long ans = 0;
	for (int i = q.size() - 1; i > 0; i--) {
		ans = max(ans, (long long)c[q[i]] * (d[q[i]] - t));
		c[f[q[i]]] += c[q[i]];
	}
	printf("%lld\n", ans);
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=899
shortest path tree
最短路树

P5122 [USACO18DEC]Fine Dining G

https://www.luogu.com.cn/problem/P5122

题目背景

题目描述

漫长的一天结束了,饥困交加的奶牛们准备返回牛棚。

农场由 NN 片牧场组成(2N5×1042\le N\le 5\times 10^4),方便起见编号为 1N1\dots N。所有奶牛都要前往位于牧场 NN 的牛棚。其他 N1N-1 片牧场中每片有一头奶牛。奶牛们可以通过 MM 条无向的小路在牧场之间移动(1M1051\le M\le 10^5)。第i条小路连接牧场 aia_ibib_i,通过需要时间 tit_i。每头奶牛都可以经过一些小路回到牛棚。

由于饥饿,奶牛们很乐于在他们回家的路上停留一段时间觅食。农场里有 KK 个有美味的干草捆,第 ii 个干草捆的美味值为 yiy_i。每头奶牛都想要在她回牛棚的路上在某一个干草捆处停留,但是她这样做仅当经过这个干草捆使她回牛棚的时间增加不超过这个干草捆的美味值。注意一头奶牛仅仅“正式地”在一个干草捆处因进食而停留,即使她的路径上经过其他放有干草捆的牧场;她会简单地无视其他的干草捆。

输入格式

输入的第一行包含三个空格分隔的整数 NNMMKK。以下 MM 行每行包含三个整数 aia_ibib_itit_i,表示牧场 aia_ibib_i 之间有一条需要 tit_i 时间通过的小路(aia_i 不等于 bib_itit_i 为不超过 10410^4 的正整数)。

以下 KK 行,每行以两个整数描述了一个干草捆:该干草捆所在牧场的编号,以及它的美味值(一个不超过 10910^9 的正整数)。同一片牧场中可能会有多个干草捆。

输出格式

输出包含 N1N-1 行。如果牧场 ii 里的奶牛可以在回牛棚的路上前往某一个干草捆并且在此进食,则第 ii 行为一个整数 11,否则为一个整数 00

样例 #1

样例输入 #1
4 5 1
1 4 10
2 1 20
4 2 3
2 3 5
4 3 2
2 7
样例输出 #1
1
1
1

提示

在这个例子中,牧场 33 里的奶牛可以停留进食,因为她回去的时间仅会增加 66(从 22 增加到 88),而这个增加量并没有超过干草捆的美味值 77。牧场 22 里的奶牛显然可以吃掉牧场 22 里的干草,因为这并不会导致她的最佳路径发生变化。对于牧场 11 里的奶牛是一个有趣的情况,因为看起来她的最佳路径(1010)可能会因为前去进食而有过大的增加量。然而,确实存在一条路径使得她可以前去干草捆处停留:先到牧场 44,然后去牧场 22(吃草),然后回到牧场 44

参考代码

#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int> > a[50020];
int n, m, k;
int d1[50020];
int d2[50020];
set<pair<int, int> > s;
void dijk(int *d) {
	for (int i = 1; i <= n; i++) {
		s.insert(make_pair(d[i], i));
	}
	while (s.size()) {
		int u = s.begin() -> second;
		s.erase(s.begin());
		for (int i = 0; i < a[u].size(); i++) {
			if (d[a[u][i].first] > d[u] + a[u][i].second) {
				s.erase(make_pair(d[a[u][i].first], a[u][i].first));
				d[a[u][i].first] = d[u] + a[u][i].second;
				s.insert(make_pair(d[a[u][i].first], a[u][i].first));
			}
		}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		a[y].push_back(make_pair(x, z));
	}
	memset(d1, 0x3f, sizeof d1);
	d1[n] = 0;
	dijk(d1);
	memset(d2, 0x3f, sizeof d2);
	for (int i = 0; i < k; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		d2[x] = d1[x] - y;
	}
	dijk(d2);
	for (int i = 1; i < n; i++) {
		printf("%d\n", d1[i] >= d2[i]);
	}
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=861
Shortest path with multi starting vertices (and different starting distance).
haybale

P1629 邮递员送信

https://www.luogu.com.cn/problem/P1629

题目背景

题目描述

有一个邮递员要送东西,邮局在节点 11。他总共要送 n1n-1 样东西,其目的地分别是节点 22 到节点 nn。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 mm 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n1n-1 样东西并且最终回到邮局最少需要的时间。

输入格式

第一行包括两个整数,nnmm,表示城市的节点数量和道路数量。

第二行到第 (m+1)(m+1) 行,每行三个整数,u,v,wu,v,w,表示从 uuvv 有一条通过时间为 ww 的道路。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

样例 #1

样例输入 #1
5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2
样例输出 #1
83

提示

对于 30%30\% 的数据,1n2001 \leq n \leq 200

对于 100%100\% 的数据,1n1031 \leq n \leq 10^31m1051 \leq m \leq 10^51u,vn1\leq u,v \leq n1w1041 \leq w \leq 10^4,输入保证任意两点都能互相到达。

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
vector<pair<int, int> > a[100020];
vector<pair<int, int> > b[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		b[y].push_back(make_pair(x, z));
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	q.push(make_pair(-d[1], 1));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < a[u.second].size(); i++) {
			pair<int, int> e = a[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 2; i <= n; i++) {
		ans += d[i];
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	q.push(make_pair(-d[1], 1));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < b[u.second].size(); i++) {
			pair<int, int> e = b[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 2; i <= n; i++) {
		ans += d[i];
	}
	printf("%d\n", ans);
	return 0;
}

题解

P4779 【模板】单源最短路径(标准版)

https://www.luogu.com.cn/problem/P4779

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

10060100 \rightarrow 60

AgCu\text{Ag} \rightarrow \text{Cu}

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 nn 个点,mm 条有向边的带非负权图,请你计算从 ss 出发,到每个点的距离。

数据保证你能从 ss 出发到任意点。

输入格式

第一行为三个正整数 n,m,sn, m, s
第二行起 mm 行,每行三个非负整数 ui,vi,wiu_i, v_i, w_i,表示从 uiu_iviv_i 有一条权值为 wiw_i 的有向边。

输出格式

输出一行 nn 个空格分隔的非负整数,表示 ss 到每个点的距离。

样例 #1

样例输入 #1
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
样例输出 #1
0 2 4 3

提示

样例解释请参考 数据随机的模板题

1n1051 \leq n \leq 10^5

1m2×1051 \leq m \leq 2\times 10^5

s=1s = 1

1ui,vin1 \leq u_i, v_i\leq n

0wi1090 \leq w_i \leq 10 ^ 9,

0wi1090 \leq \sum w_i \leq 10 ^ 9

本题数据可能会持续更新,但不会重测,望周知。

2018.09.04 数据更新 from @zzq

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, s;
vector<pair<int, int> > a[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
	scanf("%d%d%d", &n, &m, &s);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
	}
	memset(d, 0x3f, sizeof d);
	d[s] = 0;
	q.push(make_pair(-d[s], s));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < a[u.second].size(); i++) {
			pair<int, int> e = a[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		printf("%d%c", d[i], i == n ? '\n' : ' ');
	}
	return 0;
}

题解

bzoj 3040

http://www.lydsy.com/JudgeOnline/problem.php?id=3040
斐波那契堆 优化 Dijkstra
Fibonacci Heap

  1. Fast Read
    读入优化
    scanf and cin are slow
    getchar() / fread

  2. Ignore the generated edges.
    忽略生成的边

  3. Reverse all edges.
    所有边反向

Floyd

本质思路是动态规划,用f[k][i][j]表示除了起点和终点,只允许经过标号小于等于k的节点,从ij的最短路。
初始f[0][i][j]取决于ij的最短边。
转移方程

for (int k = 0; k < n; k++) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            f[k][i][j] = min(f[k-1][i][j], f[k-1][i][k]+f[k-1][k][j])
        }
    }
}

实际上第一维可以省略

for (int k = 0; k < n; k++) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            f[i][j] = min(f[i][j], f[i][k]+f[k][j])
        }
    }
}

不要记错循环的顺序,先枚举中间点。
Floyd还可以用于计算传递闭包和无向图最小环,是一个值得记住的算法。

Floyd

find the shortest paths between all pairs of vertices
任意两点之间的最短路

n times dijkstra O(m log n) * n
做n次 dijkstra 时间复杂度是 O(m log n) * n

floyd time complexity O(n^3)
floyd 时间复杂度是 O(n^3)

if m>=n2/lognm >= n^2 / \log n, then floyd is faster.
如果边数超过 m>=n2/lognm >= n^2 / \log n 那么 floyd 更快

所以 Floyd 适合在完全图上使用

完全图:任意两个点之间都有边

https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm

Dynamic Programming
动态规划的思路

d[i][j][k] means from i to j, we can only use intermediate vertices <= k,
the length the shortest path
d[i][j][k] 表示 从 ij 除了起点和终点之外,只能经过标号 <= k 的点

d[i][j][0] input graph
d[i][j][0] 输入的图,不能经过任何中间点

d[i][j][n] output
d[i][j][n] 输出的图,可以经过任何中间点

d[i][j][k] = min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])

读入 邻接矩阵
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        cin >> d[i][j];
    }
}

读入边
for (int i = 0; i < m; i++)
{
    cin >> x >> y >> z;
    d[x][y] = min(d[x][y], z);
    // multi edges
    // 可能有重边
}

// first we must enumerate the intermediate vertex
// 首先枚举中间点 k
for (int k = 0; k < n; k++) 
{
    // 枚举起点i
    for (int i = 0; i < n; i++)
    {
        // 枚举终点j
        for (int j = 0; j < n; j++)
        {
            // we can swap loop i and loop j
            // i 和 j 的循环可以交换
            d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            // d[i][j][k] = min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])
            // optimize to 2D array
            // 实际实现中并不会用三维数组,会用二维数组
        }
    }
}

P2910 [USACO08OPEN]Clear And Present Danger S

https://www.luogu.com.cn/problem/P2910

#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
int a[10020];
int d[120][120];
int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> d[i][j];
        }
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
    for (int i = 1; i < m; i++) {
        ans += d[a[i - 1]][a[i]];
    }
    cout << ans << endl;
}

CF25C

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, z;
int d[320][320];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> d[i][j];
		}
	}
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
			}
		}
	}
	cin >> m;
	for (int k = 0; k < m; k++)
	{
		cin >> x >> y >> z;
		d[x][y] = min(d[x][y], z);
		d[y][x] = min(d[y][x], z);
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][x] + d[x][j]);
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][y] + d[y][j]);
			}
		}
		long long z = 0;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j < i; j++)
			{
				z += d[i][j];
			}
		}
		cout << z << " ";
	}
	return 0;
}

Floyd算法的应用

Applications

  1. The shortest simple cycle in undirected graph.
  2. 无向图最小环

P6175 无向图的最小环问题

最小环上至少三个点,并且不能有重复的点(当然也没有重复的边)
枚举k点 是最小环中标号最大的的节点
k is largest vertex in the cycle
(u --- k --- v --- .... --- u)

假设 k 两边点是 u 和 v
那么从 v 到 u 找一条只允许只用标号 <= k-1 的最短路

u到k的边 k到v的边 v到u的路径 构成一个环,用这个环更新答案
use a[u][k] + a[k][v] + d[v][u][k-1] to update the answer

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[120][120];
int d[120][120];
int main() {
    cin >> n >> m;
    memset(a, 0x1f, sizeof a);
    for (int i = 0; i < m; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        a[x][y] = min(a[x][y], z);
        a[y][x] = min(a[y][x], z);
    }
    int ans = 0x1f1f1f1f;
    memcpy(d, a, sizeof d);
    for (int k = 1; k <= n; k++) {
        // 环上最大的点是k,和k相邻的两个点是i和j
        for (int i = 1; i < k; i++) {
            for (int j = 1; j < i; j++) {
                ans = min(ans, a[i][k] + a[k][j] + d[i][j]);
                // d[i][j]存的最短路是 从i到j,只能经过标号<k的点的最短路
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
    // 如果找不到有意义的答案就是无解
    if (ans == 0x1f1f1f1f) {
        printf("No solution.\n");
    } else {
        printf("%d\n", ans);
    }
    return 0;
}

P1119 灾后重建

https://www.luogu.com.cn/problem/P1119

#include <bits/stdc++.h>
using namespace std;
int n, m, q;
int t[220];
int d[220][220];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &t[i]);
    }
    memset(d, 0x3f, sizeof d);
    for (int i = 0; i < n; i++)
    {
        d[i][i] = 0;
    }
    for (int i = 0; i < m; i++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        d[x][y] = z;
        d[y][x] = z;
    }
    scanf("%d", &q);
    for (int l = 0, k = 0; l < q; l++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        for (; k < n && t[k] <= z; k++)
        {
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
                }
            }
        }
        if (t[x] <= z && t[y] <= z && d[x][y] < 0x3f3f3f3f)
        {
            printf("%d\n", d[x][y]);
        }
        else
        {
            printf("%d\n", -1);
        }
    }
    return 0;
}

用 bitset 做传递闭包 use bitset to maintain

P4306 [JSOI2010]连通数

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4306.cpp

P4306 [JSOI2010]连通数
if we can from i to j
如果可以从i到j
d[i][j] = 1
否则
d[i][j] = 0

读入n行n列的01矩阵
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        scanf("%1d", &d[i][j]);
    }
    d[i][i] = 1; // 自己到自己一定可以
}

直接传递闭包
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (d[i][k] && d[k][j])
            {
                d[i][j] = 1;
            }
        }
    }
}

可以调整 if 的顺序
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        if (d[i][k] == 1)
        {
            for (int j = 0; j < n; j++)
            {
                d[i][j] |= d[k][j];
            }
        }
    }
}

bitset<2000> d[2000];
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        if (d[i][k] == 1)
        {
            d[i] |= d[k];
        }
    }
}

最后数一下 d 里面有多少个 1
count how many 1s in d[][]

bitset 有函数 count 数有多少个 1

SPFA

队列优化版的Bellman Ford

基本没什么用的算法

SPFA (Only in China, useless)

use queue to optimize Bellman Ford

If there are negative edges
The time complexity is very hard to estimate.

SPFA

SPFA一般只用于费用流和判断负环,没有负环尽量使用SPFA。

其中判断负环的又分为几种情况。

  1. 从起点到终点的路上有负环,最短路为负无穷。
  2. 从起点可以到负环,但是一旦进入负环便无法到达终点,所以最短路存在。
  3. 存在负环,但是从起点无法到达。

其他变形

可能需要求解

  1. 最长路。把所有边取相反数即可
  2. 乘积最大。边权相乘,可以通过取对数转化为加法。

参考题目

Luogu P3371
最短路模板

Luogu P4779
最短路模板

Luogu P1821
正反两次最短路

Luogu P2419
传递闭包

参考资料

Dijkstra's algorithm

SPFA

P2176

https://www.luogu.com.cn/problem/P2176
[USACO11DEC]RoadBlock S / [USACO14FEB]Roadblock G/S

暴力的思路: 枚举哪条边 * 2,重新求最短路
时间复杂度 m * 最短路的时间复杂度

一定是把最短路上的某条边 * 2,只需要枚举至多 n-1 条边
We must double something in the shortest path. (There are at most n-1 edges)

枚举所有可能的边,重新计算最短路
Try all possible edges, calculate the shortest path again. Brute Froce

分层图最短路
shortest path on multi-layer graph

P4568

https://www.luogu.com.cn/problem/P4568
Shortest path on a graph, but we can use kk edges without cost.

d[i][j]. from start to i, used j free rides.
d[i][j] 从起点到i,使用了j次

https://www.luogu.com.cn/problemnew/solution/P4568

priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > points;

minimum priority queue

by default it's less<pair<int, int> >.

P2939 [USACO09FEB]Revamping Trails G

multi layer shortest path
分层图最短路

不同层之间是有向无环图
可以一层一层用 Dijkstra 做
不同层之间直接更新

solve it layer by layer

(k+1) dijsktra

P5837 [USACO19DEC]Milk Pumping G

http://usaco.org/index.php?page=viewproblem2&cpid=969

Minimize flow/cost

The number of edges is small.

Enumerate the edge with minimum flow, and then all edges with greater flow can be used.

Calculate the minimum cost. Use flow/cost to update the answer.

One time O(mlogn)O(m \log n)

In total O(m2logn)O(m^2 \log n)

P1821 [USACO07FEB]Silver Cow Party S

The shortest paths from X to the other vertices and from the other vertices to X.

Reverse every edge in the graph, and dijkstra again.

P2865 [USACO06NOV]Roadblocks G

方法1:

The (strict) second shortest path.
严格次短路

Enumerate the edge which is not in the shortest path.
次短路上的某条边一定不在最短路上

枚举这条边

starting vectex -> the start of the edge -> the end of the edge -> ending vectex.
起点 -> 枚举的边的起点 -> 枚举的边的终点 -> 终点

方法2:

https://www.luogu.com.cn/blog/Aiz-Wallenstein/solution-p2865

2 distance array.
2个距离数组

d[i] The length of shortest path
d[i] 最短距离

d2[i] The length of second shortest path
d2[i] 次短距离,必须严格大于d[i]

P3659 [USACO17FEB]Why Did the Cow Cross the Road I G

http://usaco.org/index.php?page=viewproblem2&cpid=717
Assume move 3 steps at once, so there are 16 ways to move.
In this problem, the weight is not 1, so priority_queue/set is needed.

CF938D Buy a Ticket

http://codeforces.com/problemset/problem/938/D
Given an undirected graph.

if (a[u[i]] > a[v[i]] + 2 * w[i])
{
    a[u[i]] = a[v[i]] + 2 * w[i];
}

BFS

BFS可以处理边权为1的最短路,参考单独页面 BFS

DAG中的最短路

P6145

P6145 [USACO20FEB]Timeline G
http://usaco.org/index.php?page=viewproblem2&cpid=1017

if (d[b] < d[a] + x)
{
    d[b] = d[a] + x;
}

(a, b, x) forms no cycles. Update them in topological order.

CF666B World Tour

http://codeforces.com/problemset/problem/666/B

Given a directed graph with n(<=3000) vertices and m(<=5000) edges.

The length of edges are 1.

Find 4 distinct vertices A, B, C, D

Maximize dist(A,B) + dist(B, C) + dist(C, D)

FIrst, precalculate the distance from i to every other vertices.

O(n(n+m))O(n(n + m))

For each vertex, calculate the farthest one, second farthest, third farthest vertex.

From ... to x

From x to ...

Enumerate B, C

A must be in (the farthest one, second farthest, third farthest) of B

D must be in (the farthest one, second farthest, third farthest) of C

CF986A Fair

http://codeforces.com/problemset/problem/986/A

Time complexity O(k * (n + m))
For each color, do BFS with multi starting vertices.

差分约束

简介

差分约束系统(System of Difference Constraints),是求解关于一组变数的特殊不等式组之方法。

如果一个系统由 nn 个变量和 mm 个约束条件组成,其中每个约束条件形如
xjxibk(i,j[1,n],k[1,m])x_j - x_i \leq b_k (i,j \in [1, n], k \in [1, m]),则称其为差分约束系统。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
如果xixjdx_i - x_j \leq d

那么从jjii连接一条长度为dd的边。

这样求最短路,一定满足xixj+dx_i \leq x_j + d

判断有无解

判断图有没有负环即可,如果发现了负环,说明找到了矛盾。

值得注意的是这个负环问题是全局有没有负环,所以看起来需要所有点同时入队。

求出一组解

有的时候不仅要判断一组解,而需要求出一组解。

参考题目

Luogu P1250

Luogu P1993

P1339 [USACO09OCT]Heat Wave G
P5837 [USACO19DEC]Milk Pumping G
P3659 [USACO17FEB]Why Did the Cow Cross the Road I G
P6145 [USACO20FEB]Timeline G
P1339 [USACO09OCT]Heat Wave G
P1821 [USACO07FEB]Silver Cow Party S
P2865 [USACO06NOV]Roadblocks G
CF938D Buy a Ticket
CF666B World Tour
CF986A Fair

O(k(n+m))O(k (n + m))

d[i][j] the shortest time to move good jj to vertex ii.

https://arc061.contest.atcoder.jp/tasks/arc061_c
https://www.luogu.com.cn/problem/AT2069
http://acm.hdu.edu.cn/showproblem.php?pid=6386

Wrong Solution:

https://arc061.contest.atcoder.jp/submissions/1655604

#include<iostream>
#include<set>
#include<queue>
using namespace std;
struct edge{int to,color;};
vector<edge> w[100010];
int n,m,d[100010];
set<int> s[100010];
int main()
{
    cin>>n>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        w[a].push_back({b,c});
        w[b].push_back({a,c});
    }
    d[1]=0;
    for(int i=2;i<=n;i++)d[i]=1e6;
    priority_queue<pair<int,int>> q;
    q.push({-d[1],1});
    while(q.size())
    {
        int now=q.top().second,cost=-q.top().first;q.pop();
        if(d[now]<cost) continue;
        for(edge j:w[now])
        {
            int dist=d[now];
            if(!s[now].count(j.color)) dist++;
            if(dist<d[j.to])
            {
                d[j.to]=dist;
                s[j.to].clear();
                s[j.to].insert(j.color);
                q.push({-d[j.to],j.to});
            }
            else if(dist==d[j.to])
            {
                // if (j.color not in s[j.to])
                {
                    s[j.to].insert(j.color);
                    // q.push({-d[j.to],j.to});
                }
            }
        }
    }
    if(d[n]>4e5)cout<<-1<<endl;
    else cout<<d[n]<<endl;
    return 0;
}
/*
4 6
1 2 1
2 3 1
3 4 1
1 3 2
3 2 2
2 4 3
*/
https://arc061.contest.atcoder.jp/submissions/1655604

P2939 [USACO09FEB]Revamping Trails G

不需要 long long
一层一层 做最短路

阅读其他

graph_storage
BFS
dijkstra
floyd
bellman_ford
priority_queue
diff_constraints

Dijkstra

基于贪心的思想,不能处理负权的情况,每个点额外维护一个属性,表示是否确定了最短路。

每次找到距离最短,不确定最短路的点,标记为确定了最短路,用于更新其他节点。

d[s] = 0;
for (int i = 0; i < n; i++) {
    int t = -1;
    for (int j = 0; j < n; j++) {
        if (!v[j]) {
            if (t == -1 || d[j] < d[t]) {
                t = j;
            }
        }
    }
    v[t] = 1;
    for (each edge e of t) {
        relax(e);
    }
}

这个算法可以被堆或者线段树优化,理论最优的做法是用Fibonacci堆,但是效果一般。
但是在实际实现中,往往不标记,实现成优先队列优化的SPFA。

Dijkstra

用来求最短路
Dijkstra can not deal with negative edges.
Dijkstra 不能处理负权最短路

use set or priority_queue to optimize Bellman Ford
相当于用 set 或者 priority_queue 来优化 Bellman Ford

The algorithm we used, is not exactly the same as the algorithm in the paper.
这个算法实际实现出来,并不和论文上的算法一模一样

priority_queue, we can only delete the top
priority_queue 只能删除最大值

set, we can delete anything we want
set 可以删除任意值(相当于支持修改)

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4779.cpp

in C++
by default, priority_queue top is the largest one.
默认,C++中的 priority_queue 的 top 是最大的

Traditional Version
论文上的实现方法

for (int i = 0; i < n; i++) // 循环n次
{
// find a unvisited vectex with the minimum distance
找到一个 距离最小 未标记的点

标记上他

更新所有他能到的点的距离
update all its neighbors

}

时间复杂度 O(m log n)
Time: O(m log n)

#include <bits/stdc++.h>
using namespace std;
int n, m, s;
vector<pair<int, int> > a[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
    scanf("%d%d%d", &n, &m, &s);
    for (int i = 0; i < m; i++) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        a[x].push_back(make_pair(y, z));
    }
    memset(d, 0x3f, sizeof d); // d[i] = 0x3f3f3f3f;
    // 
    d[s] = 0;
    q.push(make_pair(-d[s], s)); // q.top() is the largest
    while (q.size() > 0) {
        pair<int, int> u = q.top();
        q.pop();
        if (-u.first != d[u.second]) {
        // if u is not the newest
        // what happens if do not continue
            continue;
        }
        // if there is no negative edges.
        // for every u.second, we do it once.
        for (int i = 0; i < a[u.second].size(); i++) {
            pair<int, int> e = a[u.second][i]; // in total m times.
            if (d[e.first] > d[u.second] + e.second) {
                d[e.first] = d[u.second] + e.second;
                q.push(make_pair(-d[e.first], e.first)); // log n
                // push the new version
                // do not delete the old version
                // priority queue does not support deletion.
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        printf("%d%c", d[i], i == n ? '\n' : ' ');
    }
    return 0;
}

参考题目

spoj ADRABR

P5201 [USACO19JAN]Shortcut G

https://www.luogu.com.cn/problem/P5201

题目背景

USACO 19年一月月赛金组第三题

题目描述

每天晚上,Farmer John都会敲响一个巨大的铃铛,召唤他的奶牛们前来牛棚享用晚餐。奶牛们都急切地想要前往牛棚,所以她们都会沿着最短的路径行走。
农场可以描述为N块草地(1≤N≤10,000),方便起见编号为1…N,牛棚位于草地1。草地之间由M条双向的小路连接(N-1≤M≤50,000)。每条小路有其通过时间,从每块草地出发都存在一条由一些小路组成的路径可以到达牛棚。

草地i中有ci头奶牛。当她们听到晚餐铃时,这些奶牛都沿着一条消耗最少时间的路径前往牛棚。如果有多条路径并列消耗时间最少,奶牛们会选择其中“字典序”最小的路径(也就是说,她们通过在两条路径第一次出现分支的位置优先选择经过编号较小的草地的方式来打破并列关系,所以经过草地7、3、6、1的路径会优先于经过7、5、1的路径,如果它们所消耗的时间相同)。

Farmer John担心牛棚距离某些草地太远。他计算了每头奶牛路上的时间,将所有奶牛消耗的时间相加,称这个和为总移动时间。他想要通过额外添加一条从牛棚(草地1)连接到某块他选择的其他草地的、通过时间为T(1≤T≤10,000)的“近道”来尽可能地减少总移动时间。当一头奶牛在她平时前往牛棚的路上偶然看见这条近道时,如果这条近道能够使她更快地到达牛棚,她就会走这条路。否则,一头奶牛会仍然沿着原来的路径行走,即使使用这条近道可能会减少她的移动时间。

请帮助Farmer John求出通过添加一条近道能够达到的总移动时间减少量的最大值。

输入格式

输入的第一行包含N、M和T。以下N行包含c1…cN,均为0…10,000范围内的整数。以下M行,每行由三个整数a,b和t描述了一条小路,这条小路连接了草地a和b,通过时间为t。所有的通过时间在1…25,000范围内。

输出格式

输出Farmer John可以达到的总移动时间的最大减少量。

样例 #1

样例输入 #1
5 6 2
1 2 3 4 5
1 2 5
1 3 3
2 4 3
3 4 5
4 5 2
3 5 7
样例输出 #1
40

提示

参考代码

#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int> > a[10020];
int n, m, t;
int c[10020];
int d[10020];
int f[10020];
set<pair<int, int> > s;
vector<int> q;
int main() {
	scanf("%d%d%d", &n, &m, &t);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &c[i]);
	}
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		a[y].push_back(make_pair(x, z));
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	s.insert(make_pair(d[1], 1));
	while (s.size()) {
		int u = s.begin() -> second;
		q.push_back(u);
		s.erase(s.begin());
		for (int i = 0; i < a[u].size(); i++) {
			if (d[a[u][i].first] > d[u] + a[u][i].second) {
				s.erase(make_pair(d[a[u][i].first], a[u][i].first));
				d[a[u][i].first] = d[u] + a[u][i].second;
				f[a[u][i].first] = u;
				s.insert(make_pair(d[a[u][i].first], a[u][i].first));
			} else if (d[a[u][i].first] == d[u] + a[u][i].second) {
				f[a[u][i].first] = min(f[a[u][i].first], u);
			}
		}
	}
	long long ans = 0;
	for (int i = q.size() - 1; i > 0; i--) {
		ans = max(ans, (long long)c[q[i]] * (d[q[i]] - t));
		c[f[q[i]]] += c[q[i]];
	}
	printf("%lld\n", ans);
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=899
shortest path tree
最短路树

P5122 [USACO18DEC]Fine Dining G

https://www.luogu.com.cn/problem/P5122

题目背景

题目描述

漫长的一天结束了,饥困交加的奶牛们准备返回牛棚。

农场由 NN 片牧场组成(2N5×1042\le N\le 5\times 10^4),方便起见编号为 1N1\dots N。所有奶牛都要前往位于牧场 NN 的牛棚。其他 N1N-1 片牧场中每片有一头奶牛。奶牛们可以通过 MM 条无向的小路在牧场之间移动(1M1051\le M\le 10^5)。第i条小路连接牧场 aia_ibib_i,通过需要时间 tit_i。每头奶牛都可以经过一些小路回到牛棚。

由于饥饿,奶牛们很乐于在他们回家的路上停留一段时间觅食。农场里有 KK 个有美味的干草捆,第 ii 个干草捆的美味值为 yiy_i。每头奶牛都想要在她回牛棚的路上在某一个干草捆处停留,但是她这样做仅当经过这个干草捆使她回牛棚的时间增加不超过这个干草捆的美味值。注意一头奶牛仅仅“正式地”在一个干草捆处因进食而停留,即使她的路径上经过其他放有干草捆的牧场;她会简单地无视其他的干草捆。

输入格式

输入的第一行包含三个空格分隔的整数 NNMMKK。以下 MM 行每行包含三个整数 aia_ibib_itit_i,表示牧场 aia_ibib_i 之间有一条需要 tit_i 时间通过的小路(aia_i 不等于 bib_itit_i 为不超过 10410^4 的正整数)。

以下 KK 行,每行以两个整数描述了一个干草捆:该干草捆所在牧场的编号,以及它的美味值(一个不超过 10910^9 的正整数)。同一片牧场中可能会有多个干草捆。

输出格式

输出包含 N1N-1 行。如果牧场 ii 里的奶牛可以在回牛棚的路上前往某一个干草捆并且在此进食,则第 ii 行为一个整数 11,否则为一个整数 00

样例 #1

样例输入 #1
4 5 1
1 4 10
2 1 20
4 2 3
2 3 5
4 3 2
2 7
样例输出 #1
1
1
1

提示

在这个例子中,牧场 33 里的奶牛可以停留进食,因为她回去的时间仅会增加 66(从 22 增加到 88),而这个增加量并没有超过干草捆的美味值 77。牧场 22 里的奶牛显然可以吃掉牧场 22 里的干草,因为这并不会导致她的最佳路径发生变化。对于牧场 11 里的奶牛是一个有趣的情况,因为看起来她的最佳路径(1010)可能会因为前去进食而有过大的增加量。然而,确实存在一条路径使得她可以前去干草捆处停留:先到牧场 44,然后去牧场 22(吃草),然后回到牧场 44

参考代码

#include <bits/stdc++.h>
using namespace std;
vector<pair<int, int> > a[50020];
int n, m, k;
int d1[50020];
int d2[50020];
set<pair<int, int> > s;
void dijk(int *d) {
	for (int i = 1; i <= n; i++) {
		s.insert(make_pair(d[i], i));
	}
	while (s.size()) {
		int u = s.begin() -> second;
		s.erase(s.begin());
		for (int i = 0; i < a[u].size(); i++) {
			if (d[a[u][i].first] > d[u] + a[u][i].second) {
				s.erase(make_pair(d[a[u][i].first], a[u][i].first));
				d[a[u][i].first] = d[u] + a[u][i].second;
				s.insert(make_pair(d[a[u][i].first], a[u][i].first));
			}
		}
	}
}
int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		a[y].push_back(make_pair(x, z));
	}
	memset(d1, 0x3f, sizeof d1);
	d1[n] = 0;
	dijk(d1);
	memset(d2, 0x3f, sizeof d2);
	for (int i = 0; i < k; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		d2[x] = d1[x] - y;
	}
	dijk(d2);
	for (int i = 1; i < n; i++) {
		printf("%d\n", d1[i] >= d2[i]);
	}
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=861
Shortest path with multi starting vertices (and different starting distance).
haybale

P1629 邮递员送信

https://www.luogu.com.cn/problem/P1629

题目背景

题目描述

有一个邮递员要送东西,邮局在节点 11。他总共要送 n1n-1 样东西,其目的地分别是节点 22 到节点 nn。由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有 mm 条道路。这个邮递员每次只能带一样东西,并且运送每件物品过后必须返回邮局。求送完这 n1n-1 样东西并且最终回到邮局最少需要的时间。

输入格式

第一行包括两个整数,nnmm,表示城市的节点数量和道路数量。

第二行到第 (m+1)(m+1) 行,每行三个整数,u,v,wu,v,w,表示从 uuvv 有一条通过时间为 ww 的道路。

输出格式

输出仅一行,包含一个整数,为最少需要的时间。

样例 #1

样例输入 #1
5 10
2 3 5
1 5 5
3 5 6
1 2 8
1 3 8
5 3 4
4 1 8
4 5 3
3 5 6
5 4 2
样例输出 #1
83

提示

对于 30%30\% 的数据,1n2001 \leq n \leq 200

对于 100%100\% 的数据,1n1031 \leq n \leq 10^31m1051 \leq m \leq 10^51u,vn1\leq u,v \leq n1w1041 \leq w \leq 10^4,输入保证任意两点都能互相到达。

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
vector<pair<int, int> > a[100020];
vector<pair<int, int> > b[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
		b[y].push_back(make_pair(x, z));
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	q.push(make_pair(-d[1], 1));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < a[u.second].size(); i++) {
			pair<int, int> e = a[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 2; i <= n; i++) {
		ans += d[i];
	}
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	q.push(make_pair(-d[1], 1));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < b[u.second].size(); i++) {
			pair<int, int> e = b[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 2; i <= n; i++) {
		ans += d[i];
	}
	printf("%d\n", ans);
	return 0;
}

题解

P4779 【模板】单源最短路径(标准版)

https://www.luogu.com.cn/problem/P4779

题目背景

2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。

然后呢?

10060100 \rightarrow 60

AgCu\text{Ag} \rightarrow \text{Cu}

最终,他因此没能与理想的大学达成契约。

小 F 衷心祝愿大家不再重蹈覆辙。

题目描述

给定一个 nn 个点,mm 条有向边的带非负权图,请你计算从 ss 出发,到每个点的距离。

数据保证你能从 ss 出发到任意点。

输入格式

第一行为三个正整数 n,m,sn, m, s
第二行起 mm 行,每行三个非负整数 ui,vi,wiu_i, v_i, w_i,表示从 uiu_iviv_i 有一条权值为 wiw_i 的有向边。

输出格式

输出一行 nn 个空格分隔的非负整数,表示 ss 到每个点的距离。

样例 #1

样例输入 #1
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
样例输出 #1
0 2 4 3

提示

样例解释请参考 数据随机的模板题

1n1051 \leq n \leq 10^5

1m2×1051 \leq m \leq 2\times 10^5

s=1s = 1

1ui,vin1 \leq u_i, v_i\leq n

0wi1090 \leq w_i \leq 10 ^ 9,

0wi1090 \leq \sum w_i \leq 10 ^ 9

本题数据可能会持续更新,但不会重测,望周知。

2018.09.04 数据更新 from @zzq

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, s;
vector<pair<int, int> > a[100020];
priority_queue<pair<int, int> > q;
int d[100020];
int main() {
	scanf("%d%d%d", &n, &m, &s);
	for (int i = 0; i < m; i++) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[x].push_back(make_pair(y, z));
	}
	memset(d, 0x3f, sizeof d);
	d[s] = 0;
	q.push(make_pair(-d[s], s));
	while (q.size() > 0) {
		pair<int, int> u = q.top();
		q.pop();
		if (-u.first > d[u.second]) {
			continue;
		}
		for (int i = 0; i < a[u.second].size(); i++) {
			pair<int, int> e = a[u.second][i];
			if (d[e.first] > d[u.second] + e.second) {
				d[e.first] = d[u.second] + e.second;
				q.push(make_pair(-d[e.first], e.first));
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		printf("%d%c", d[i], i == n ? '\n' : ' ');
	}
	return 0;
}

题解

bzoj 3040

http://www.lydsy.com/JudgeOnline/problem.php?id=3040
斐波那契堆 优化 Dijkstra
Fibonacci Heap

  1. Fast Read
    读入优化
    scanf and cin are slow
    getchar() / fread

  2. Ignore the generated edges.
    忽略生成的边

  3. Reverse all edges.
    所有边反向

SPFA

队列优化版的Bellman Ford

基本没什么用的算法

SPFA (Only in China, useless)

use queue to optimize Bellman Ford

If there are negative edges
The time complexity is very hard to estimate.

SPFA

SPFA一般只用于费用流和判断负环,没有负环尽量使用SPFA。

其中判断负环的又分为几种情况。

  1. 从起点到终点的路上有负环,最短路为负无穷。
  2. 从起点可以到负环,但是一旦进入负环便无法到达终点,所以最短路存在。
  3. 存在负环,但是从起点无法到达。

其他变形

可能需要求解

  1. 最长路。把所有边取相反数即可
  2. 乘积最大。边权相乘,可以通过取对数转化为加法。

参考题目

Luogu P3371
最短路模板

Luogu P4779
最短路模板

Luogu P1821
正反两次最短路

Luogu P2419
传递闭包

参考资料

Dijkstra's algorithm

SPFA

Floyd

本质思路是动态规划,用f[k][i][j]表示除了起点和终点,只允许经过标号小于等于k的节点,从ij的最短路。
初始f[0][i][j]取决于ij的最短边。
转移方程

for (int k = 0; k < n; k++) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            f[k][i][j] = min(f[k-1][i][j], f[k-1][i][k]+f[k-1][k][j])
        }
    }
}

实际上第一维可以省略

for (int k = 0; k < n; k++) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            f[i][j] = min(f[i][j], f[i][k]+f[k][j])
        }
    }
}

不要记错循环的顺序,先枚举中间点。
Floyd还可以用于计算传递闭包和无向图最小环,是一个值得记住的算法。

Floyd

find the shortest paths between all pairs of vertices
任意两点之间的最短路

n times dijkstra O(m log n) * n
做n次 dijkstra 时间复杂度是 O(m log n) * n

floyd time complexity O(n^3)
floyd 时间复杂度是 O(n^3)

if m>=n2/lognm >= n^2 / \log n, then floyd is faster.
如果边数超过 m>=n2/lognm >= n^2 / \log n 那么 floyd 更快

所以 Floyd 适合在完全图上使用

完全图:任意两个点之间都有边

https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm

Dynamic Programming
动态规划的思路

d[i][j][k] means from i to j, we can only use intermediate vertices <= k,
the length the shortest path
d[i][j][k] 表示 从 ij 除了起点和终点之外,只能经过标号 <= k 的点

d[i][j][0] input graph
d[i][j][0] 输入的图,不能经过任何中间点

d[i][j][n] output
d[i][j][n] 输出的图,可以经过任何中间点

d[i][j][k] = min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])

读入 邻接矩阵
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        cin >> d[i][j];
    }
}

读入边
for (int i = 0; i < m; i++)
{
    cin >> x >> y >> z;
    d[x][y] = min(d[x][y], z);
    // multi edges
    // 可能有重边
}

// first we must enumerate the intermediate vertex
// 首先枚举中间点 k
for (int k = 0; k < n; k++) 
{
    // 枚举起点i
    for (int i = 0; i < n; i++)
    {
        // 枚举终点j
        for (int j = 0; j < n; j++)
        {
            // we can swap loop i and loop j
            // i 和 j 的循环可以交换
            d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            // d[i][j][k] = min(d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1])
            // optimize to 2D array
            // 实际实现中并不会用三维数组,会用二维数组
        }
    }
}

P2910 [USACO08OPEN]Clear And Present Danger S

https://www.luogu.com.cn/problem/P2910

#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
int a[10020];
int d[120][120];
int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> d[i][j];
        }
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
    for (int i = 1; i < m; i++) {
        ans += d[a[i - 1]][a[i]];
    }
    cout << ans << endl;
}

CF25C

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, z;
int d[320][320];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			cin >> d[i][j];
		}
	}
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
			}
		}
	}
	cin >> m;
	for (int k = 0; k < m; k++)
	{
		cin >> x >> y >> z;
		d[x][y] = min(d[x][y], z);
		d[y][x] = min(d[y][x], z);
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][x] + d[x][j]);
			}
		}
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				d[i][j] = min(d[i][j], d[i][y] + d[y][j]);
			}
		}
		long long z = 0;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j < i; j++)
			{
				z += d[i][j];
			}
		}
		cout << z << " ";
	}
	return 0;
}

Floyd算法的应用

Applications

  1. The shortest simple cycle in undirected graph.
  2. 无向图最小环

P6175 无向图的最小环问题

最小环上至少三个点,并且不能有重复的点(当然也没有重复的边)
枚举k点 是最小环中标号最大的的节点
k is largest vertex in the cycle
(u --- k --- v --- .... --- u)

假设 k 两边点是 u 和 v
那么从 v 到 u 找一条只允许只用标号 <= k-1 的最短路

u到k的边 k到v的边 v到u的路径 构成一个环,用这个环更新答案
use a[u][k] + a[k][v] + d[v][u][k-1] to update the answer

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[120][120];
int d[120][120];
int main() {
    cin >> n >> m;
    memset(a, 0x1f, sizeof a);
    for (int i = 0; i < m; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        a[x][y] = min(a[x][y], z);
        a[y][x] = min(a[y][x], z);
    }
    int ans = 0x1f1f1f1f;
    memcpy(d, a, sizeof d);
    for (int k = 1; k <= n; k++) {
        // 环上最大的点是k,和k相邻的两个点是i和j
        for (int i = 1; i < k; i++) {
            for (int j = 1; j < i; j++) {
                ans = min(ans, a[i][k] + a[k][j] + d[i][j]);
                // d[i][j]存的最短路是 从i到j,只能经过标号<k的点的最短路
            }
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
            }
        }
    }
    // 如果找不到有意义的答案就是无解
    if (ans == 0x1f1f1f1f) {
        printf("No solution.\n");
    } else {
        printf("%d\n", ans);
    }
    return 0;
}

P1119 灾后重建

https://www.luogu.com.cn/problem/P1119

#include <bits/stdc++.h>
using namespace std;
int n, m, q;
int t[220];
int d[220][220];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &t[i]);
    }
    memset(d, 0x3f, sizeof d);
    for (int i = 0; i < n; i++)
    {
        d[i][i] = 0;
    }
    for (int i = 0; i < m; i++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        d[x][y] = z;
        d[y][x] = z;
    }
    scanf("%d", &q);
    for (int l = 0, k = 0; l < q; l++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        for (; k < n && t[k] <= z; k++)
        {
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
                }
            }
        }
        if (t[x] <= z && t[y] <= z && d[x][y] < 0x3f3f3f3f)
        {
            printf("%d\n", d[x][y]);
        }
        else
        {
            printf("%d\n", -1);
        }
    }
    return 0;
}

用 bitset 做传递闭包 use bitset to maintain

P4306 [JSOI2010]连通数

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4306.cpp

P4306 [JSOI2010]连通数
if we can from i to j
如果可以从i到j
d[i][j] = 1
否则
d[i][j] = 0

读入n行n列的01矩阵
for (int i = 0; i < n; i++)
{
    for (int j = 0; j < n; j++)
    {
        scanf("%1d", &d[i][j]);
    }
    d[i][i] = 1; // 自己到自己一定可以
}

直接传递闭包
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            if (d[i][k] && d[k][j])
            {
                d[i][j] = 1;
            }
        }
    }
}

可以调整 if 的顺序
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        if (d[i][k] == 1)
        {
            for (int j = 0; j < n; j++)
            {
                d[i][j] |= d[k][j];
            }
        }
    }
}

bitset<2000> d[2000];
for (int k = 0; k < n; k++)
{
    for (int i = 0; i < n; i++)
    {
        if (d[i][k] == 1)
        {
            d[i] |= d[k];
        }
    }
}

最后数一下 d 里面有多少个 1
count how many 1s in d[][]

bitset 有函数 count 数有多少个 1

最小生成树

两个最小生成树(Minimum Spanning Tree, MST)算法完全等价。

如果输入邻接矩阵,一般用Prim。

如果输入边表,一般用Kruskal。

性质

环性质(Cycle property)
对于一个环C,如果其中一条边e的边权严格大于C中其他所有的边,那么这条边不可能属于最小生成树。
For any cycle C in the graph, if the weight of an edge e of C is larger than the individual weights of all other edges of C, then this edge cannot belong to an MST.

割性质(Cut property)
对于一个割C,如果其中一条边e的边权严格小于C中其他所有的边,那么这条边一定属于最小生成树。
For any cut C of the graph, if the weight of an edge e in the cut-set of C is strictly smaller than the weights of all other edges of the cut-set of C, then this edge belongs to all MSTs of the graph.

Borůvka's algorithm

对于每个点,权值最小的边一定会被选择

参考题目

Luogu P1265
Prim最小生成树(完全图,邻接矩阵)

Luogu P2872
Prim最小生成树(完全图,邻接矩阵)但是需要考虑权值为0的边

Luogu P2212
Prim最小生成树(完全图,邻接矩阵)如果边权小于c,那么返回++\infty

Luogu P1546
最小生成树(邻接矩阵) 推荐Prim,也可以Kruskal

Luogu P3366
Kruskal最小生成树

Luogu P1111
Kruskal求最小生成树最大边

Luogu P2820
Kruskal最小生成树,求所有不选的边的权值之和

Luogu P1547
Kruskal求最小生成树最大边

Luogu P2916
Kruskal求最小生成树,边权需要转化

参考资料

https://en.wikipedia.org/wiki/Minimum_spanning_tree

https://en.wikipedia.org/wiki/Prim's_algorithm

https://en.wikipedia.org/wiki/Kruskal's_algorithm

最小生成树 (Minimum Spanning Tree)

Prim

Prim's algorithm

可以使用堆优化,类似Dijkstra,但是使用堆优化,效率未必提高。

对于完全图的情况:

不使用堆优化,时间复杂度为O(n2)O(n^2)

使用堆优化,时间复杂度为O(n2logn)O(n^2 \log n)

所以说对于以邻接矩阵输入的图,没必要去用Kruskal。

参考代码(Luogu P1265):

#include <bits/stdc++.h>
using namespace std;
int n;
double x[5020], y[5020];
double d[5020];
bool v[5020];
double ans;
double dist(int i, int j) {
    // 返回点i和点j之间的距离。
    return hypot(x[i] - x[j], y[i] - y[j]);
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
    }
    for (int i = 1; i < n; i++) {
        d[i] = 1e30;
    }
    // 除了0号点,每个点距离都初始化为无穷大。
    for (int i = 0; i < n; i++) {
        int mini = -1;
        double mind = 1e30;
        for (int j = 0; j < n; j++) {
            if (!v[j]) {
                if (mind > d[j]) {
                    mind = d[j];
                    mini = j;
                }
            }
        }
        // 每次找到未被标记的点中,距离最近的点。
        ans += mind;
        v[mini] = true;
        // 选择连向这个点的边,并标记这个点已被选择。
        for (int j = 0; j < n; j++) {
            if (!v[j]) {
                d[j] = min(d[j], dist(j, mini));
                // 更新剩余所有点的距离。
            }
        }
    }
    cout << fixed << setprecision(2) << ans << endl;
    return 0;
}

O(n2)O(n^2)

n is the number of vertices.

The edge with minimum weight in a cut must be in MST.

2 arrays
visited, whether we have chosen this vertex.
distance, the minimum distance from this vertex to a chosen vertex.

do n times
find the vertex min_i with minimum distance, it is not chosen. n * O(n) -> n * O(log n)
ans += d[min_i]
v[min_i]
for each edge starting from min_i to j, the length dist(min_i, j) O(m) -> O(m log m)
d[j] = min(d[j], dist(min_i, j))

If the graph is a complete graph, we will use prim's algorithm.

Prim:只对完全图使用
时间复杂度O(n^2)
但是不能输入n^2个数字,只能输入n个,所以距离是通过某种方式算出来的

需要2个数组
v数组,标记数组,表示每个点是否已经被选择
d数组,距离数组,表示每个点到已经选择的集合的距离的最小值是多少

v数组初始化0,表示都没有被选择
d数组初始化无穷大,因为没已经选择的集合
d中初的起点始化为0,保证第一个选择的是起点

做n轮 // 每个点恰好被选1次
找到d数组中,未被标记的位置中的最小值d[min_i],还需要记录最小值的位置min_i
ans += 最小值d[min_i]
标记最小值的位置min_i为选择 v[min_i] = true
枚举从min_i出发的所有的边(min_i, j, dist(min_i, j)),
如果j没有被标记过
更新d[j] = min(d[j], dist(min_i, j))
// 根据需要,考虑是否需要记录是谁更新的j

ans = 0 + 最小生成树上(n-1)个边权
d数组中存的是0和最小生成树上(n-1)个边权

Kruskal's algorithm

先对所有边排序,从小到大依次选择,能选则选,选不了就放弃,用并查集判断能否选择。

可以求最小生成森林,也就是算法只执行到合适的连通块数就推出。

时间复杂度和把所有边排序的复杂度一样,为O(mlogm)O(m \log m)

如果只需要求最小生成树的最大边的权值,可以在O(m)O(m)的时间内求出。
(二分,每次丢掉一半)

参考代码(Luogu P3366):

#include <bits/stdc++.h>
using namespace std;
int f[5020];
int n, m;
long long ans;
pair<int, pair<int, int> > a[200020];
int find(int x) {
    return f[x] != x ? f[x] = find(x) : x;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        f[i] = i;
    }
    for (int i = 0; i < m; i++) {
        cin >> a[i].second.first >> a[i].second.second >> a[i].first;
        // 读入边的两个端点和边权。
    }
    sort(a, a + m);
    // 按照边权(first)从小到大排序。
    for (int i = 0; i < m; i++) {
        int x = find(a[i].second.first);
        int y = find(a[i].second.second);
        if (x != y) {
        // 如果这条边的左右端点不在同一个集合内,那么选择这条边,并合并他们。
            f[x] = y;
            ans += a[i].first;
        }
    }
    cout << ans << endl;
    return 0;
}

Kruskal

https://wwwwodddd.com/turing/mst.md
P1546 [USACO3.1]最短网络 Agri-Net
P1547 [USACO05MAR]Out of Hay S
P5425 [USACO19OPEN]I Would Walk 500 Miles G
P2906 [USACO08OPEN]Cow Neighborhoods G
P2916 [USACO08NOV]Cheering up the Cow G
P2212 [USACO14MAR]Watering the Fields S
P2872 [USACO07DEC]Building Roads S

P4643 [国家集训队]阿狸和桃子的游戏
P1265 公路修建
P3366 【模板】最小生成树
P2121 拆地毯
P1195 口袋的天空
P1396 营救

Kruskal
复杂度排序所有的边 m log m

  1. minimize the longest edge
  2. minimize the sum of edges
  3. minimize the second longest edge

Kruskal

O(mlogm)O(m \log m)

m is the number of edges

sort the edges,

Merge the two end points of edges by union find set.

The edge with maximum weight in a cycle can't be in MST.

The number of vertices = The number of edges + 1

http://poj.org/problem?id=2395
P1547 [USACO05MAR]Out of Hay S

http://usaco.org/index.php?page=viewproblem2&cpid=946
P5425 [USACO19OPEN]I Would Walk 500 Miles G
MST by Prim, or analysis the properties

P4643 [国家集训队]阿狸和桃子的游戏

nn vertices with weight, nn is even. mm edges with weight.

2 players color the vertices in turn.

Finally, each player will color n/2n/2 vertices.

The final score = the sum of vertex weight + the sum of edges whose both vertices are colored by the player.

The first player want to maximize (score of player 1)-(score of player 2)

The second player want to maximize (score of player 2)-(score of player 1)

Add half of the edge weight to the vertex weight

greedy algorithm

http://poj.org/problem?id=1258
P1546 [USACO3.1]最短网络 Agri-Net
MST

P2212 [USACO14MAR]Watering the Fields S
points on a plane, Euclid distance, find MST
segments less than C can't be chosen.

P2872 [USACO07DEC]Building Roads S
points on a plane, Euclid distance, find MST
Some points are already connected

P2916 [USACO08NOV]Cheering up the Cow G
W[j] = 2 * L[j] + C[S[j]] + C[E[j]]
Find the MST

http://poj.org/problem?id=2485
adjacent matrix, find the longest edge in MST.

http://poj.org/problem?id=2560

P1265 公路修建
points on a plane, Euclid distance, find MST
There are n2n^2 edges.
n^2条边不可能都输入

We need some ways to generate the edge weights.
需要一个算法生成这些边权

http://poj.org/problem?id=1789
Truck History
Complete Graph

dist(i, j) = edit distance s[i], s[j]

http://poj.org/problem?id=3723
Conscription
n + m soldiers
To collect a soldier without any privilege, 10000

For edge(x, y, d)
To collect y when x is collected, 10000 - d

Find the minimum cost

最小生成树
既是 边权之和最小的生成树
也是 最大边最小的生成树
也是 次大边最小的生成树

如果是求最小生成森林(不需要最小生成树,可以剩下k个连通块)
也是Kruskal

P3366 【模板】最小生成树
模板题
ans += a[i].first

P1547 [USACO05MAR]Out of Hay S
ans = a[i].first

P2121 拆地毯
最大生成森林

P1195 口袋的天空
最小生成森林

P1396 营救
加入每条边后判断
if (F(s) == F(t)) print answer, break
可以证明最优解一定是在最小生成树上走

P1967 货车运输
上一个题目的多组询问版
需要先求最大生成树
然后在最大生成树上询问路径最小值

CF609E
和 P1967 几乎一模一样
求最小生成树
对于树边,答案直接是MST的边权和
对于非树边,答案是 选择这条边 删去树上的路径中的最大边。

CF160D

P1111 修复公路
有无解的情况
no solution

P2820 局域网

P2126 Mzc家中的男家丁

P4047 [JSOI2010]部落划分
如果k = 2,那么答案是最小生成树最大边(第n-1小边)
如果k = n,那么答案是距离最近的2个节点,也就是最小生成树的最小边(第n-1大边)
求最小生成树的第k-1大边,也是第n-k+1小边

Prim
P1194 买礼物

P2906 [USACO08OPEN]Cow Neighborhoods G

(x1, y1)
(x2, y2)

|x1 - x2| + |y1 + y2| =

(a1 = x1 + y1, b1 = x1 - y1)
(a2 = x2 + y2, b2 = x2 - y2)

max(|a1 - a2|, |b1 - b2|)

if the points are sorted
max(a1 - a2, b1 - b2) for all b2 <= b1
max(a1 - a2, b2 - b1) for all b2 >= b1

max(a1 - a2, b1 - b2) <= C for all b2 <= b1
max(a1 - a2, b2 - b1) <= C for all b2 >= b1

a1 - a2 <= C && b1 - b2 <= C for all b2 <= b1
a1 - a2 <= C && b2 - b1 <= C for all b2 >= b1

There are n items, the i-th is aia_i.

If we have the i-th item, we can use bi,jb_{i,j} to buy the j-th item.

We want to buy all the items.

What is the minimum cost.

Prim

Prim's algorithm

可以使用堆优化,类似Dijkstra,但是使用堆优化,效率未必提高。

对于完全图的情况:

不使用堆优化,时间复杂度为O(n2)O(n^2)

使用堆优化,时间复杂度为O(n2logn)O(n^2 \log n)

所以说对于以邻接矩阵输入的图,没必要去用Kruskal。

参考代码(Luogu P1265):

#include <bits/stdc++.h>
using namespace std;
int n;
double x[5020], y[5020];
double d[5020];
bool v[5020];
double ans;
double dist(int i, int j) {
    // 返回点i和点j之间的距离。
    return hypot(x[i] - x[j], y[i] - y[j]);
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x[i] >> y[i];
    }
    for (int i = 1; i < n; i++) {
        d[i] = 1e30;
    }
    // 除了0号点,每个点距离都初始化为无穷大。
    for (int i = 0; i < n; i++) {
        int mini = -1;
        double mind = 1e30;
        for (int j = 0; j < n; j++) {
            if (!v[j]) {
                if (mind > d[j]) {
                    mind = d[j];
                    mini = j;
                }
            }
        }
        // 每次找到未被标记的点中,距离最近的点。
        ans += mind;
        v[mini] = true;
        // 选择连向这个点的边,并标记这个点已被选择。
        for (int j = 0; j < n; j++) {
            if (!v[j]) {
                d[j] = min(d[j], dist(j, mini));
                // 更新剩余所有点的距离。
            }
        }
    }
    cout << fixed << setprecision(2) << ans << endl;
    return 0;
}

O(n2)O(n^2)

n is the number of vertices.

The edge with minimum weight in a cut must be in MST.

2 arrays
visited, whether we have chosen this vertex.
distance, the minimum distance from this vertex to a chosen vertex.

do n times
find the vertex min_i with minimum distance, it is not chosen. n * O(n) -> n * O(log n)
ans += d[min_i]
v[min_i]
for each edge starting from min_i to j, the length dist(min_i, j) O(m) -> O(m log m)
d[j] = min(d[j], dist(min_i, j))

If the graph is a complete graph, we will use prim's algorithm.

Prim:只对完全图使用
时间复杂度O(n^2)
但是不能输入n^2个数字,只能输入n个,所以距离是通过某种方式算出来的

需要2个数组
v数组,标记数组,表示每个点是否已经被选择
d数组,距离数组,表示每个点到已经选择的集合的距离的最小值是多少

v数组初始化0,表示都没有被选择
d数组初始化无穷大,因为没已经选择的集合
d中初的起点始化为0,保证第一个选择的是起点

做n轮 // 每个点恰好被选1次
找到d数组中,未被标记的位置中的最小值d[min_i],还需要记录最小值的位置min_i
ans += 最小值d[min_i]
标记最小值的位置min_i为选择 v[min_i] = true
枚举从min_i出发的所有的边(min_i, j, dist(min_i, j)),
如果j没有被标记过
更新d[j] = min(d[j], dist(min_i, j))
// 根据需要,考虑是否需要记录是谁更新的j

ans = 0 + 最小生成树上(n-1)个边权
d数组中存的是0和最小生成树上(n-1)个边权

Kruskal's algorithm

先对所有边排序,从小到大依次选择,能选则选,选不了就放弃,用并查集判断能否选择。

可以求最小生成森林,也就是算法只执行到合适的连通块数就推出。

时间复杂度和把所有边排序的复杂度一样,为O(mlogm)O(m \log m)

如果只需要求最小生成树的最大边的权值,可以在O(m)O(m)的时间内求出。
(二分,每次丢掉一半)

参考代码(Luogu P3366):

#include <bits/stdc++.h>
using namespace std;
int f[5020];
int n, m;
long long ans;
pair<int, pair<int, int> > a[200020];
int find(int x) {
    return f[x] != x ? f[x] = find(x) : x;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        f[i] = i;
    }
    for (int i = 0; i < m; i++) {
        cin >> a[i].second.first >> a[i].second.second >> a[i].first;
        // 读入边的两个端点和边权。
    }
    sort(a, a + m);
    // 按照边权(first)从小到大排序。
    for (int i = 0; i < m; i++) {
        int x = find(a[i].second.first);
        int y = find(a[i].second.second);
        if (x != y) {
        // 如果这条边的左右端点不在同一个集合内,那么选择这条边,并合并他们。
            f[x] = y;
            ans += a[i].first;
        }
    }
    cout << ans << endl;
    return 0;
}

定义

如果一个无向简单图GG满足以下相互等价的条件之一,那么GG是一棵树:

  1. GG是没有回路的连通图。
  2. GG没有回路,但是在GG内添加任意一条边,就会形成一个回路。
  3. GG内的任意两个顶点能被唯一路径所连通。

如果无向简单图GG有有限个顶点(设为nn个顶点),那么GG是一棵树还等价于:

  1. GG是连通的,有n1n - 1条边,并且GG没有简单回路。

性质

一棵树中每两个点之间都有且只有一条路径(指没有重复边的路径)。一颗有N个点的树有N-1条边,也就是连接N个点所需要的最少边数。所以如果去掉树中的一条边,树就会不连通。

如果在一棵树中加入任意的一条边,就会得到有且只有一个环的图。这是因为这条边连接的两个点(或是一个点)中有且只有一条路径,这条路径和新加的边连在一起就是一个环。如果把一个连通图中的多余边全部删除,所构成的树叫做这个图的生成树。

如果要在树中加入一个点,就要加入一条这个点和原有的点相连的边。这条边不会给这棵树增加一个环或者多余的路径。所以每次这样加入一个点,就可以构成一棵树。

一棵树既可以是有向的也可以是无向的。显然,树是连通图,但不会是双连通图(对于无向图)或者强连通图(对于有向图)。树可以算是稀疏图。

显然树中也没有自环和重复边。

存储

树的父亲表示法

对于普通的树,可以像图一样为每一个点存储一个边表(通常按顺序存和每一个点的关系的叫做邻接矩阵,存具体的边的叫做邻接表),或者直接存储所有边的边表等。由于树是稀疏图,所以一般不用邻接矩阵存储。对于有根树,如果用为每一个点储存一个边表的方法,由于每一棵树都只有一个父节点,所以通常指向父节点的边不存在这个表中。同时如果子节点是没有顺序的,也是因为一个节点的子节点不会同时是其他节点的子节点,也可以把子节点直接当成存边的链表的节点,这时候每个节点只需要储存两个指针,所以这种存储方法有时候也会被叫做多叉树转二叉树。

对于子节点是有顺序的有根树,每条边都可以以固定的位置分别储存。对于完全二叉树甚至能直接用一个数组访问所有节点,不另外储存边的信息。有的树可以被设计成固定的从根节点开始访问,这时候可以不储存父节点。同样的,有的树也可以省略子节点,例如并查集。

树的遍历

对于一般的树,可以用和普通的图一样的方法遍历,比如深度优先搜索和宽度优先搜索。如果和树的每个节点相邻的点有固定的顺序,深度优先搜索可以不储存当前点以外的任何信息,而且不用判重。而在有根树中更方便,所以有根树中很少使用宽度优先搜索。

对于有根树的从根开始的深度优先搜索遍历,有三种特定的顺序:

注意对于每一种遍历,事实上都得先访问根节点,这里的遍历顺序是指处理节点中的数据的顺序。已知中序遍历和任一其他遍历的情况下,可以还原一个二叉树。一个直观的方法是按前序或者反转的后序插入一个按中序排序的搜索树。已知前序和中序也可以还原一棵树,但是不能知道二叉树中一个节点唯一的子树是在左边还是右边。

事实上也可以把左右的顺序反过来。这些由根开始的遍历方法也适用于特定的一个子树。

森林

森林比树少一个条件,指没有回路的图。也就是说,森林可能是不连通的,边数可能比树还少,就是说小于顶点数。

森林也可以看成是好多棵互不相连的非空的树,只有一棵树也可以算是森林。不过森林不一定是一棵树。

森林也可以是有根的,这时候森林中的每一棵树都有一个根。

一棵树去掉若干条边也会成为森林,这时候可以看成一棵树分成了好多棵树。森林中的两棵树之间加一条边,也可以把这两棵树合在一起,最终合成一棵树。

很多地方用森林,都是用来表示很多棵树,包括作为逻辑结构、数据结构的时候等。有一种重要的数据结构并查集就是一个有根的森林,可以很快的判断两个元素是不是属于同一个互相独立的集合,以及合并两个集合等。

其他介绍

父亲节点(parent):一个节点到根节点路径上第二个节点

祖先节点(ancestor):一个节点到根节点路径上的节点

二叉树

二叉树是一种特殊的树,甚至一些定义和传统的树有矛盾。

完全二叉树

满二叉树

DFS/BFS

我们可以通过DFS计算出一个树中所有点的深度。

LCA

参考题目

参考资料

https://en.wikipedia.org/wiki/Tree_(graph_theory)

树的直径

树的重心

2个定义
1. 删掉自己之后,最大的子树最小 / 最大的子树<=n/2
2. 重心到所有点的距离之和最小

P3379,P5836,P6098,P4374,P3128

Lowest Common Ancestor 最近公共祖先

2 ways to brute forces, find LCA(x, y) 2种暴力方式

  1. 先标记x的所有祖先节点,y向上跳,直到遇到一个被标记的节点。
    mark the ancestors of x, go up from y, the first marked vertex is LCA.
  2. 先把x和y调整成深度一样的,然后当x和y不同,同时向上跳。
    adjust x and y to the same depth. while (x != y) x = parent[x], y = parent[y]

一些有意义的算法
Some meaningful algorithms

  1. 倍增,O(nlogn)O(n \log n)预处理,单个询问 O(logn)O(\log n)
    binary lifting, O(n log n) preprocess, O(log n) per query
  2. Tarjan求LCA,是一个离线时间复杂度 O(n+q)O(n + q)
    Tarjan Offline, Time Complexity O(n+q)O(n + q)
  3. DFS序+RMQ,时间复杂度 O(nlogn)O(n log n) 预处理,单个询问 O(1)O(1)
    DFS order + RMQ, O(nlogn)O(n \log n) preprocess, O(1)O(1) per query
  4. 树链剖分,O(n)预处理,单个询问 O(logn)O(\log n)
    Tree decomposition, O(n) preprocess, O(logn)O(\log n) per query
  5. 理论最优,O(n)预处理,单个询问 O(1)O(1)
    Theoretically optimal solution, online O(n+q)O(n + q)

parent[x]

一些题目

f[x][i]

f[x][0] = parent[x]
f[x][1] = parent[parent[x]]
f[x][2] = parent[parent[parent[parent[x]]]]
f[x][y] = f[f[x][y-1]][y-1]

from x go up 2**i steps, 

parent[parent[parent[... parent[x]]]]
2**i parents.


from x go up 17 steps

f[f[x][0]][4]

https://oi-wiki.org/graph/hld/

为什么 树链剖分 好
树链剖分的最坏情况,二叉树
一般不会有人主动去构造二叉树

随机一个树,深度不会太深,所以暴力LCA其实很快
为了卡暴力,会生成一些比较长的链
树链剖分 可以 BFS 空间 O(n)

倍增算法和树链剖分最有用
binary lifting and tree decomposition are the most useful.
他们都是在线,并且足够块
they are online and fast enough.

倍增算法是最容易理解和实现的算法
binary lifting is easy to understand and easy to debug.
树链剖分很快
tree decomposition is fast.
树链剖分的最坏情况是二叉树,不是长链
The worst case of tree decomposition is binary tree, not a long chain.
出题人一般不会出二叉树
problem setters are not likely to create such data tests.

P3379 【模板】最近公共祖先(LCA)
binary lifting

DFS and RMQ

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3379__.cpp

P4281 [AHOI2008]紧急集合 / 聚会

询问 a, b, c
x = lca(a, b)
y = lca(b, c)
z = lca(c, a)

集合点: x ^ y ^ z
总路程: d[a] + d[b] + d[c] - d[x] - d[y] - d[z]

http://www.usaco.org/index.php?page=viewproblem2&cpid=970

https://www.luogu.com.cn/problem/P5836

P5838 [USACO19DEC]Milk Visits G

http://www.usaco.org/index.php?page=viewproblem2&cpid=921

https://www.luogu.com.cn/problem/P6098

P6098 [USACO19FEB]Cow Land G

DFS order and Binary Indexed Tree

5
1 2
1 3
3 4
3 5

 1 3 4 4 5 5 3 2 2 1
 + + + - + - - + - -
[ ]

+w[1] +w[3] +w[4] -w[4] +w[5] = + w[1] + w[3] + w[5] 
from start to L[x]   is from x to the root
+ - can be changed to xor (the operation must be invertible)

two usages of DFS order.
1. increase at first occurence, decrease at last occurence, prefix = path to root
2. only increase at first occurence, subtree = interval


to query the path from a vertex to root
1. DFS order
2. Tree Decomposition

Tree Decomposition
The number of chains are the number of leafs
For any two vertices, we want minize the number of chains between them


 heavy-light decomposition O(log(n))
 longest decomposition.  O(sqrt(n))

Segment tree is a superset of binary indexed tree, except 2D version.

http://www.usaco.org/current/data/sol_cowland_gold_feb19.html

This solution is weird, it uses tree decomposition to maintain the path to root.

but use another binary lifting

http://www.usaco.org/index.php?page=viewproblem2&cpid=948

Tree Boxes

https://www.luogu.com.cn/problem/P4374

P4374 [USACO18OPEN]Disruption P

DFS and merge by size

Diameter

2 DFS

find the farthest vertex of root, v1

find the farthest vertex of v1, v2

v1, v2 is the diameter.

There might be a solution about coloring.

http://usaco.org/index.php?page=viewproblem2&cpid=866

Every time, we remove a vertex with degree(in the tree) <= 1, and in degree 0.

The last one we removed is a possible solution.

https://www.luogu.com.cn/problem/P3128

P3128 [USACO15DEC]Max Flow P

Difference and prefix sum

https://codeforces.com/contest/191/problem/C

191C - Fools and Roads

difference and prefix sum on a tree

https://codeforces.com/contest/519/problem/E

519E - A and B and Lecture Rooms

Find the midpoint

https://codeforces.com/contest/609/problem/E

609E - Minimum spanning tree for each edge

Find the maximum edge on a path in the tree

https://codeforces.com/contest/176/problem/E

176E - Archaeology

Find total length of the subtree

https://codeforces.com/problemset/problem/1328/E

1328E - Tree Queries

use dfs order to judge ancestor

O(1) judge whether x is an ancestor of y (by DFS order)

If interval x contains interval y, then x is an ancestor of y.

https://www.luogu.com.cn/problem/SP3978

https://www.luogu.com.cn/problem/SP14932

P4427 [BJOI2018]求和

P5002 专心OI - 找祖先

自己有大小为a, b, c的三个子树
那么自己的答案是

(1 * a + (1 + a) * b + (1 + a + b) * c) * 2 + 1

(1 + a + b + c) * (1 + a + b + c) - a * a - b * b - c * c

CF1328E Tree Queries
每个点跳到自己的父亲节点
链越长越好/从跟节点连到最深的节点最好
其他节点必须是最深的节点的祖先,否则NO

所有点进栈最靠后的位置 <= 所有点出栈最靠前的位置 那么YES

判断x和y是什么关系

  1. x和y相同
  2. x是y的祖先
  3. y是x的祖先
  4. x和y没有关系

做法
5
1 2
1 3
3 4
3 5

DFS序有2种

  1. 进出栈序
    1 2 2 3 4 4 5 5 3 1
    长度为2n,每个点恰好出现2次

每个点等价于一个区间

  1. x和y相同,区间相同

  2. x是y的祖先,x的区间包含y的区间

  3. y是x的祖先,y的区间包含x的区间

  4. x和y没有关系,两个区间相离
    区间不可能相交

  5. 欧拉序
    1 2 1 3 4 3 5 3 1
    最后一个1可以不加,长度为(2n-2)
    每个点恰好出现度数次

Codechef ALLIANCE

对于每个帮派求LCA。

对于询问的联盟求LCA,有三个情况。

  1. 联盟的LCA和首都并不是祖先关系,直接计算距离。
  2. 是祖先关系,并且首都下面也有,距离为00
  3. 倍增向上跳,找到第一次相遇的地方。

树链剖分
漆子超《分治算法在树的路径问题中的应用》

  1. 一般是把所有的重链串在一起建线段树
  2. 如果只是需要求LCA,不需要建重链

可以求出一个序列,既是DFS序,又是轻重链的序列
P3178 [HAOI2015]树上操作
P2146 [NOI2015]软件包管理器

spoj POLICEMEN

强连通分量

Tarjan 算法

每个点维护 dfs 和 low 两个标记

dfs时遇到每个点,有3种情况

  1. 从未被访问过 !dfn[y]
    tarjan(y);
    low[x] = min(low[x], low[y])
  2. 已经被访问,还未出栈 v[y]
    low[x] = min(low[x], dfn[y])
  3. 已经被访问,已经出栈
    忽略

对于每个点,枚举所有出边之后
如果 dfn == low
那么当前子树是一个强连通分量

Kosaraju 算法

P1726 上白泽慧音
P1262 间谍网络
P2002 消息扩散

P2272 [ZJOI2007]最大半连通子图
P2656 采蘑菇
P6030 [SDOI2012]走迷宫

双连通分量

割边
https://www.luogu.com.cn/problem/T103489

割点
从根节点 DFS

  1. 如果自己是根节点,自己有多于1个孩子,那么自己是割点。
  2. 如果自己不是根节点,如果存在一个孩子 low[孩子] >= dfn[自己] 那么自己是割点

拓扑排序

简介

在计算机科学领域,有向图的拓扑排序是其顶点的线性排序,使得对于从顶点 uu 到顶点 vv 的每个有向边 uvuvuu 在排序中都在 vv 之前。 例如,图形的顶点可以表示要执行的任务,并且边可以表示一个任务必须在另一个任务之前执行的约束; 在这个应用中,拓扑排序只是一个有效的任务顺序。 如果且仅当图形没有定向循环,即如果它是有向无环图(DAG),则拓扑排序是可能的。 任何 DAG 具有至少一个拓扑排序,并且已知这些算法用于在线性时间内构建任何 DAG 的拓扑排序。

用一种容器(比如栈,队列,集合)维护当前所有入度为00的点。
每次从容器中取出一个点,删掉他和他的出边,这可能导致一些点入度为00,将新的入度为00的点加入容器。

根据需要,也有可能需要将所有边反向,以倒序生成拓扑排序。

也可以使用DFS生成拓扑排序。

简介

只有有向无环图可以拓扑排序
排序之后,排名靠前的点指向排名靠后的
时间复杂度 O(n + m)

BFS的写法

排名第一 的点一定入度为0
不断删除入度为0的点,更新其他点的入度

DFS的写法

倒序生成拓扑排序
如果想放x,必须把x指向的节点都先放好

一个不太好做的问题

输入一个有向无环图。
问哪些点在拓扑序中的位置是确定的。

似乎只能O(nm)O(nm)暴力。

拓扑排序和动态规划

拓扑排序和动态规划有密不可分的关系。
所有动态规划的所有状态,一定构成一个有向无环图,并且以拓扑序进行更新。

练习题

abc223_d

https://atcoder.jp/contests/abc223/tasks/abc223_d
求一个长度为n的排列,输入m个限制
第i个限制是ai必须在bi之前
排列字典序越小越好

枚举m
    读入x和y认为是从x到y的一条边
    a[x].push_back(y) // 记录下所有从x出发的边
    y的入度++

枚举所有点i
    如果点i的入度为0
        把i加入集合

当集合非空
    拿出集合中最小的数字x,作为排列的下一个数字
    删去这个数字x
    枚举所有从x出发的边
        这个边指向的点,入度-=1
        如果减成了0
            这个点入集合

如果排列凑齐了n个数字
    那么输出
否则无解
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y;
vector<int> a[200020];
int d[200020];
set<int> s;
vector<int> z;
int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;
        a[x].push_back(y);
        d[y]++;
    }
    for (int i = 1; i <= n; i++)
    {
        if (d[i] == 0)
        {
            s.insert(i);
        }
    }
    while (s.size() > 0)
    {
        int x = *s.begin();
        z.push_back(x);
        s.erase(s.begin());
        for (int i: a[x])
        {
            if (--d[i] == 0)
            {
                s.insert(i);
            }
        }
    }
    if (z.size() == n)
    {
        for (int i: z)
        {
            cout << i << " ";
        }
    }
    else
    {
        cout << -1 << endl;
    }
    return 0;
}

abc245_f Endless Walk

https://atcoder.jp/contests/abc245/tasks/abc245_f
输入一个有向图,N个点M条边
问有多少个点,作为起点,可以找到无限长的路径?

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, z;
vector<int> a[200020];
int d[200020];
queue<int> q;
int main()
{
    cin >> n >> m;
    z = n;
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;
        a[y].push_back(x);
        d[x]++;
    }
    for (int i = 1; i <= n; i++)
    {
        if (d[i] == 0)
        {
            q.push(i);
        }
    }
    while (q.size())
    {
        int u = q.front();
        q.pop();
        z--;
        for (int i : a[u])
        {
            if (!--d[i])
            {
                q.push(i);
            }
        }
    }
    cout << z << endl;
    return 0;
}

abc216_d Pair of Balls

https://atcoder.jp/contests/abc216/tasks/abc216_d
一共有n种球,每种球2个。这2n个球构成m个栈,如果某种球的两个球都在栈顶,可以同时删掉这两个球,问能不能把所有球删完。

icpc2012autumn_a Dictionary

https://atcoder.jp/contests/jag2012autumn/tasks/icpc2012autumn_a
输入n个字符串,你来决定字母之间的顺序,让这n个字符串有序。

CF510C Fox And Names

相邻2个单词可以得到一条边

abcdy
abcdx

a
ab
这样的无法得到任意边
ab
a
这样的直接无解

CF825E Minimal Labels

堆优化,拓扑排序

原题要求
点1在拓扑排序中的位置尽量小
点2在拓扑排序中的位置尽量小
点3在拓扑排序中的位置尽量小
...
点n在拓扑排序中的位置尽量小

等价于

让拓扑排序中的第n个点尽量大
让拓扑排序中的第n-1个点尽量大
...
让拓扑排序中的第1个点尽量大

CF883B Berland Army

CF915D Almost Acyclic Graph

直接暴力做O(m^2)
转化为枚举点,拓扑排序
时间复杂度O(n(n + m))

CF1100E Andrew and Taxi

二分,改变等价于删除

CF1217D Coloring Edges

有环:2
无环:1

P1038 神经网络

拓扑排序,动态规划

P1347 排序

枚举所有前缀,求拓扑序
判断拓扑序列是否唯一
每次出队之后,队列大小必须是0

P1983 车站分级

经过的>没经过的,拓扑排序

数据范围比较小,可以暴力
一个优化,通过加点,减少建的边的个数

P2017 [USACO09DEC]Dizzy Cows G

根据有向边定向,

#include <bits/stdc++.h>
using namespace std;
int n, p1, p2, x, y;
vector<int> a[100020];
int in[100020];
int pos[100020], cnt;
int main() {
    cin >> n >> p1 >> p2;
    for (int i = 0; i < p1; i++) {
        cin >> x >> y;
        a[x].push_back(y);
        in[y]++;
    }
    queue<int> q; // stack
    for (int i = 1; i <= n; i++) {
        if (in[i] == 0) {
            q.push(i);
        }
    }
    while (q.size() > 0) {
        int u = q.front(); // top
        q.pop();
        pos[u] = cnt++;
        for (int i : a[u])
        {
            if (!--in[i])
            {
                q.push(i);
            }
        }
        // for (int i = 0; i < a[u].size(); i++) {
        //     in[a[u][i]]--;
        //     if (in[a[u][i]] == 0) {
        //         q.push(a[u][i]);
        //     }
        // }
    }
    for (int i = 0; i < p2; i++) {
        cin >> x >> y;
        if (pos[x] < pos[y]) {
            cout << x << ' ' << y << endl;
        } else {
            cout << y << ' ' << x << endl;
        }
    }
    return 0;
}

P3183 [HAOI2016]食物链

拓扑排序动态规划

P3243 [HNOI2015]菜肴制作

优先队列,拓扑排序
希望 第一个做出的菜标号尽量小
希望 1所在的位置尽量靠前

P4376 [USACO18OPEN]Milking Order G

二分,拓扑排序
如果可以满足前X条件,那么一定可以满足前X-1个条件
如果不能满足前X条件,那么一定不能满足前X+1个条件

#include <bits/stdc++.h>
using namespace std;
vector<int> a[100020];
vector<int> b[100020];
vector<int> z;
int n, m;
int in[100020];
priority_queue<int, vector<int>, greater<int> > q;
stack<int> s;
int size()
{
    return q.size() + s.size();
}
void push(int x, bool f)
{
    if (f)
    {
        s.push(x);
    }
    else
    {
        q.push(x);
    }
}
int top(bool f)
{
    int x;
    if (f)
    {
        x = s.top();
        s.pop();
    }
    else
    {
        x = q.top();
        q.pop();
    }
    return x;
}
int ok(int x, bool f)
{
    for (int i = 1; i <= n; i++)
    {
        a[i].clear();
        in[i] = 0;
    }
    for (int i = 0; i < x; i++)
    {
        for (int j = 1; j < b[i].size(); j++)
        {
            a[b[i][j - 1]].push_back(b[i][j]);
            in[b[i][j]]++;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        if (in[i] == 0)
        {
            push(i, f);
        }
    }
    z.clear();
    while (size())
    {
        z.push_back(top(f));
        for (int i: a[z.back()])
        {
            if (!--in[i])
            {
                push(i, f);
            }
        }
    }
    return z.size() == n;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++)
    {
        int l, x;
        scanf("%d", &l);
        for (int j = 0; j < l; j++)
        {
            scanf("%d", &x);
            b[i].push_back(x);
        }
    }
    int L = 0;
    int R = m + 1;
    while (L < R - 1)
    {
        int M = (L + R) / 2;
        (ok(M, 1) ? L : R) = M;
    }
    ok(L, 0);
    for (int i = 0; i < n; i++)
    {
        printf("%d%c", z[i], i == n - 1 ? '\n' : ' ');
    }
    return 0;
}

P6145 [USACO20FEB]Timeline G

拓扑排序,最长路

d[i] >= s[i]

d[b] >= d[a] + x
d[y] >= d[x] + z
d[i.first] < d[x] + i.second
<  =

对于每个i,都min(d[i])
#include <bits/stdc++.h>
using namespace std;
int n, m, c, x, y, z;
vector<pair<int, int> > a[100020];
int d[100020];
int in[100020];
queue<int> q;
int main()
{
    scanf("%d%d%d", &n, &m, &c);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &d[i]);
    }
    for (int i = 0; i < c; i++)
    {
        scanf("%d%d%d", &x, &y, &z);
        a[x].push_back(make_pair(y, z));
        in[y]++;
    }
    for (int i = 1; i <= n; i++)
    {
        if (in[i] == 0)
        {
            q.push(i);
        }
    }
    while (q.size() > 0)
    {
        int u = q.front();
        q.pop();
        for (pair<int, int> i: a[u])
        {
            if (d[i.first] < d[u] + i.second)
            {
                d[i.first] = d[u] + i.second;
            }
            if (!--in[i.first])
            {
                q.push(i.first);
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
        printf("%d\n", d[i]);
    }
    return 0;
}

P5603 小C与桌游

https://www.luogu.com.cn/problem/P5603

动态规划

经典题目

数字三角形

最长不下降子序列

最长公共子序列

线性状态动态规划

P2758 编辑距离
P1279 字串距离

区间动态规划

状态是一个区间

树形动态规划

背包

有向无环图动态规划 DAG

状态压缩动态规划

子集
决策一个排列,直接暴力 n! * n, 动态规划 2^n * n^2  f[i(i 是一个集合)] -> f[i | (1 << j)]
决策一个排列,但是要记录最后一个是什么  f[i(i 是一个集合)][j] -> f[i | (1 << k)][k]
每一行的状态压缩成一个数字,两行之间暴力枚举,检查是否可以转移
插头动态规划,逐格转移

数位动态规划

前导0    P1590 失踪的7
int F(int n) # <n的答案,如果需要 <=n 的答案,调用F(n+1)
12345
0????
10???
11???
120??
121??
122??
1230?
1231?
1232?
1233?
12340
12341
12342
12343
12344

(2,3) (3,5) (5,10) (10,2) (2,3) (3,5) (5,10) (10,2)


P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

对于每个位置 第i行 第j列
记录 f[i][j] 起点到 第i行 第j列 的最大值

状态的含义

  1. 从(1, 1)到(i, j)的最大值
  2. 从(i, j)到第n行的最大值

转移也有两种写法

  1. 自己在(i, j) 一定是从(i-1,j-1) 或 (i-1,j) 到自己
  2. 自己在(i, j) 下一步可以到(i+1,j) 或 (i+1,j+1)

P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles
贪心是错的
每次走较大的一边 错误

动态规划 2种思路
从 起点 到 (i, j) 记录最优解 f[i][j]
f[i][j] = max(f[i-1][j-1], f[i-1][j]) + a[i][j]
7
10 15
18 16 15
20 25 20 19
24 30 27 26 24

从 (i, j) 到 终点 记录最优解 f[i][j]
f[i][j] = max(f[i+1][j], f[i+1][j+1]) + a[i][j]
30
23 21
20 13 10
7 12 10 10
4 5 2 6 5

P2871 [USACO07DEC]Charm Bracelet S
P2639 [USACO09OCT]Bessie's Weight Problem G
P5196 [USACO19JAN]Cow Poetry G

P6120 [USACO17JAN]Hoof, Paper, Scissor S
P3609 [USACO17JAN]Hoof, Paper, Scissor G
P5124 [USACO18DEC]Teamwork G
P5424 [USACO19OPEN]Snakes G

记忆化搜索

P1434 [SHOI2002]滑雪
P1464 Function

背包

P1048 采药
P1616 疯狂的采药
P1164 小A点菜

图中没有环
最优子结构 (如果希望最终的结果 mod 100最大呢?)
无后效性 (如果要求路径上的中位数最大 相当于到(i, j)只记录和不够)

最长不下降子序列 / 最长公共子序列

https://www.spoj.com/problems/HMLIS/
最长上升子序列的个数

P5156

https://www.luogu.com.cn/problem/P5156
为什么删去的是第k小的集合,剩下的一定是第k的集合
求字典序第k大的最长不下降子序列

3 2 1 6 5 4 9 8 7

求第13大的 最长不下降子序列 是什么?
以3开始的 最长不下降子序列 长度是多少? 方案数多少? 长度是3,方案数是9
方案数不够,13 -= 9 求剩下的中,第4大

以2开始的 最长不下降子序列 长度是多少? 方案数多少? 长度是3,方案数是9
方案数够了,求剩下的中,第4大

第一位是2
下一位是什么呢?

以6开始的 最长不下降子序列 长度是多少? 方案数多少? 长度是2,方案数是3
方案数不够,4 -= 3 求剩下的中,第1大

以5开始的 最长不下降子序列 长度是多少? 方案数多少? 长度是2,方案数是3
方案数够了,求剩下的中,第1大

第二位是5
下一位是什么呢?

以9开始的 最长不下降子序列 长度是多少? 方案数多少? 长度是1,方案数是1
方案数够了

第三位是9

每一个数字,如果可能出现在LIS中,那么他在LIS中的位置是唯一的

1 4 7 2 5 8 3 6 9

LIS长度是5
以自己为开始 LIS长度是5的哪些数字 可能 1
以自己为开始 LIS长度是4的哪些数字 可能 4 2
以自己为开始 LIS长度是3的哪些数字 可能 7 5 3
以自己为开始 LIS长度是2的哪些数字 可能 8 6
以自己为开始 LIS长度是1的哪些数字 可能 9

每一位的可能性,都是降序的
比如在第二位选择了2,
那么第三位不可能选择7

8 8 7 7 9 9
1 2 3 4 5 6

4 3 2 1 6 5 排序之后

最长公共子序列 / 编辑距离 / ……
https://www.luogu.com.cn/problem/P6119
f[i][j] 表示第一个序列的前i个,和第二个序列的前j个,的最优解

https://www.luogu.com.cn/problem/P1279
f[i][j] 表示第一个序列的前i个,和第二个序列的前j个,的最优解

https://www.luogu.com.cn/problem/P1115
P1115 最大子段和

f[i] 表示以 a[i] 结尾的最大子段和
f[i] = max(f[i-1], 0) + a[i]

P1181 数列分段Section I

P5124 [USACO18DEC]Teamwork G
f[i] best solution of the first i cows

for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}

for (int i = 1; i <= n; i++)
{
int mx = 0;
for (int j = i - 1; j >= i - k && j >= 0; j++) // last group [j, i-1] length i-j
{
mx = max(mx, a[j]);
f[i] = max(f[i], f[j] + (i-j) * mx);
}
}

P5424 [USACO19OPEN]Snakes G

区间动态规划
前缀

http://usaco.org/index.php?page=viewproblem2&cpid=897
USACO 2019 January Contest, Gold
Problem 1. Cow Poetry

http://usaco.org/index.php?page=viewproblem2&cpid=863
USACO 2018 December Contest, Gold
Problem 3. Teamwork

f[i] 前i个的最优解

状态数O(n)
转移数O(k)
转移时间O(1)

http://usaco.org/index.php?page=viewproblem2&cpid=945
USACO 2019 US Open Contest, Gold
Problem 1. Snakes

f[i][j] 前i个蛇,用了j个大小,最优解是什么

https://www.luogu.com.cn/problem/P6120
P6120 [USACO17JAN]Hoof, Paper, Scissor S

https://www.luogu.com.cn/problem/P3609
P3609 [USACO17JAN]Hoof, Paper, Scissor G

不能
f[i][j] 前i轮,变换了j次,最多赢几局
这样做的话需要枚举最后一种手势,用了多少轮,时间复杂度O(n^2)太慢了

f[i][j][c]
前i轮,变换了j次,最后一轮出的c,最多平局几次

动态规划更新时有2种写法

  1. 考虑哪些状态可以转移到自己
  2. 考虑自己可以转移到哪些状态

动态规划有2种写法

  1. 递推
    一般用递推写

  2. 递归(记忆化搜索)
    有的题目不容易决定动态规划的顺序

https://www.luogu.com.cn/problem/P3146
https://www.luogu.com.cn/problem/P3147
如果起点i确定,凑出的值确定,终点位置是确定的

2 2 2 4

树形动态规划
状态一般是子树怎么怎么样

http://usaco.org/index.php?page=viewproblem2&cpid=897
USACO 2019 January Contest, Gold
Problem 1. Cow Poetry

http://usaco.org/index.php?page=viewproblem2&cpid=863
USACO 2018 December Contest, Gold
Problem 3. Teamwork

http://usaco.org/index.php?page=viewproblem2&cpid=945
USACO 2019 US Open Contest, Gold
Problem 1. Snakes

f[i][j]
First i integers, used j different sizes
minimize the wasted space.

consider the range of last size
a[k+1], a[k+2], …, a[i]
f[i][j] = min(f[i][j], f[k][j – 1] + (mx[k + 1][i] * (i – k) – (s[i] – s[k])));
First k integers, used j-1 different sizes + the wasted space of (a[k+1], a[k+2], …, a[i])

the number of states * the number of transitions * the time of a transition

mx[i][j] = max(mx[i][j - 1], a[j]);
max(a[i], a[i+1], ..., a[j-1], a[j]) = max(max(a[i], a[i+1], ..., a[j-1]), a[j])

http://usaco.org/index.php?page=viewproblem2&cpid=791
USACO 2018 January Contest, Gold
Problem 3. Stamp Painting

Find the total number of plans
Find the number of invalid plans

Knapsack

  1. 01 knapsack problem

  2. unbounded knapsack problem

  3. optimize / maximize

  4. counting

https://www.luogu.com.cn/problem/P2925
01 Knapsack problem
Bessie's Weight Problem

include <bits/stdc++.h>

using namespace std;
int n, m, x;
bitset<50020> f;
int main() {
scanf("%d%d", &m, &n);
f[0] = 1;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
f |= f << x;
// f[x+0] |= f[0]
// f[x+1] |= f[1]
// f[x+2] |= f[2]
...
// f[m] |= f[m-x]
}
for (int i = m; i >= 0; i--) {
// find the largest i, such that f[i] == 1 && i <= m
if (f[i]) {
printf("%d\n", i);
break;
}
}
return 0;
}

include <bits/stdc++.h>

using namespace std;
int n, m, x;
int f[50020];
int main() {
scanf("%d%d", &m, &n);
f[0] = 1;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
for (int j = m; j >= x; j--)
{
f[j] |= f[j - x];
}
}
for (int i = m; i >= 0; i--) {
// find the largest i, such that f[i] == 1 && i <= m
if (f[i]) {
printf("%d\n", i);
break;
}
}
return 0;
}

http://usaco.org/index.php?page=viewproblem2&cpid=839
USACO 2018 US Open Contest, Gold
Problem 3. Talent Show

f[i] the total weight is i, what is the maximum talent.
TLE their total weight is very large.

f[i] the total talent is i, what is the minimum weight.
WA for some total talent, if we choose the minimum weight, it is < w

Binary search the ratio, for each cow, it has a new value t[i] - r * w[i]
we want to find a subset
total weight >= W
total new value >= 0
now we can do DP on weight.

O(W * n)

Binary Search

  1. integer
  2. real numbers / control the number of times.

1
2
3
4
5
6

6
5
4
3
2
1

a[i] the position of i in the first sequence
b[i] the position of i in the second sequence

if abs(i - j) <= 4:
add new point(a[i], b[j])

P4084 [USACO17DEC]Barn Painting G

http://usaco.org/current/data/sol_hps_gold_jan17.html

spoj REDRONESIA

f[i][j] length i, first j chars, the number of words

f[i][j + 1] += f[i][j]
f[i + 1][j + 1] += f[i][j]
f[i + 2][j + 1] += f[i][j]

spoj ADACON

spoj TPGA

spoj SWAPDIFF1

3 4 6 5 1 2
2 4 6 5 1 3

5 6 1 2 4 3
5 1 6 2 4 3

xi > 0
x1 + x2 + ... + xk = n
the number of solutions is C(n-1, k-1)

xi >= 0
(x1 + 1) + (x2 + 1) + ... + (xk + 1) = n + k
the number of solutions is C(n+k-1, k-1)

xi > ai
(x1 - a1) + (x2 - a2) + ... + (xk - ak) = n - (a1 + a2 + ... + ak)
the number of solutions is C(n - (a1 + a2 + ... + ak), k-1)

2 >= xi >= 0
x1 + x2 + ... + xk = n
the number of solutions is C(n+k-1, k-1)

b1 >= x1 > 0
b2 >= x2 > 0
b3 >= x3 > 0

x1 + x2 + x3 = n
the number of solutions

if the first part < 0, it is 0

(1)
x1 > 0
x2 > 0
x3 > 0

(2)
x1 > b1
x2 > 0
x3 > 0

(3)
x1 > 0
x2 > b2
x3 > 0

[] - [b1] - [b2] - [b3] + [b1, b2] + [b1, b3] + [b2, b3] - [b1, b2, b3]

CF1238E Keyboard Purchase
bitmasks/dp

f[i]
the first several chars are set i

#include<bits/stdc++.h>
using namespace std;const int N=(1<<21)+3;
int dp[N],g[20][20],c,n,m,i,j,k;char s[N];
int main(){
    for(scanf("%d%d%s",&n,&m,s+1),i=2;i<=n;++i)g[s[i]-'a'][s[i-1]-'a']=++g[s[i-1]-'a'][s[i]-'a'];
    // read the string
    // g['a']['b'] means the number of times, 'a' is next to 'b'

    for(i=1;i<(1<<m);++i)
    {
        dp[i]=2e9;
        for(j=c=0;j<m;++j)
            for(k=j+1;k<m;++k)
                if((i>>j&1)^(i>>k&1))
                    // for every pair of char
                    // if j and k are at different sides
                    c+=g[j][k];
        for(j=0;j<m;++j)
            if(i>>j&1)
                dp[i]=min(dp[i],dp[i^(1<<j)]+c);
        for(j=0;j<m;++j)
            if(~i>>j&1)
                dp[i|1<<j]=min(dp[i|1<<j],dp[i]+c);
    }printf("%d\n",dp[(1<<m)-1]);
}

CF11D A Simple Task
bitmasks/dp
start at the smallest vertex

f[i][j]
start from the lowbit of i, pass the set i, end at j, the number of plans

if lowbit of i, is connected with j, add f[i][j] to the answer

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int

using namespace std;

int n,m,t,u,v;
bool a[19][19]; //存边
ll ans,f[600001][19]; //状压

inline int qr(){ //快读
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch^48);
    return res;
}

int main(){
    n=qr(),m=qr();t=1<<n;
    for(rg i=1;i<=m;++i){
        u=qr()-1;v=qr()-1;
        a[u][v]=a[v][u]=1;//加边
    }
    for(rg i=0;i<n;++i)
        f[1<<i][i]=1; //初始化(创建以i为起点的路径)
    for(rg i=0;i<t;++i){
        // i is a vertex set
        for(rg j=0;j<n;++j){
            if(!f[i][j])continue; //加速
            // j is the last vertex of the path
            // j should be in i
            for(rg k=0;k<n;++k){
                if(!a[j][k])continue; //加速
                // k should not be in i
                // from j to k
                if((i&-i)>1<<k)continue; //起点不能改!!!(去重)
                // k can't be less than the start point
                if(1<<k&i)
                { //这个点走过
                    if((1<<k)==(i&-i)) //起点与终点相同
                    {
                        if k is the start point of the path
                        from lowbit(i) to j to k is a cycle.
                        ans+=f[i][j];
                    }
                }
                else
                {
                    f[i|1<<k][k]+=f[i][j]; //没走过就走!
                }
            }
        }
    }printf("%lld",(ans-m)/2); //处理之后再输出!
    // ignore the same edge twice
    // devided by two.
    return 0;
}

http://poj.org/problem?id=2411
another famous problem

https://www.luogu.com.cn/problem/P4799
https://ceoi2015.fi.muni.cz/day2/eng/day2task1-eng.pdf
split-half search
find all subsets of the first half
2 ** (n/2)

find all subsets of the second half
2 ** (n/2)

do two-pointer

we can not
for (int i = 1; i < 1 << n; i++)
{
for (int j = 0; j < n; j++)
{
if (i >> j & 1)
{
s[i] += a[j];
}
}
}

0100011110111000
0100011110110000
0000000000001000

http://www.usaco.org/index.php?page=viewproblem2&cpid=647
USACO 2016 US Open Contest, Gold
Problem 3. 248
f[i][j]

merge a[i] ... a[j] into 1 number
the largest result

1 1 1 1 3 2 2 -> 4
2 + 2 + 2 + 2 + 8 + 4 + 4 = 16

2 1 2 -> -1

this is wrong:
for (left end point)
for (right end point)
[1, 1]
[1, 2]
[2, 2]

for (length of interval)
for (start point)

import java.io.;
import java.util.
;
public class two48 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("248.in"));
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("248.out")));
int n = Integer.parseInt(br.readLine());
int[] list = new int[n];
for(int i = 0; i < n; i++) {
list[i] = Integer.parseInt(br.readLine());
}
int[][] dp = new int[n][n];
int ret = 0;
for(int len = 1; len <= n; len++) {
for(int i = 0; i + len <= n; i++) {
int j = i+len-1;
dp[i][j] = -1;
if(len == 1) {
dp[i][j] = list[i];
}
for(int k = i; k < j; k++) {
if(dp[i][k] == dp[k+1][j] && dp[i][k] > 0) {
dp[i][j] = dp[i][k] + 1;
}
}
ret = Math.max(ret, dp[i][j]);
}
}
pw.println(ret);
pw.close();
}
}

http://www.usaco.org/index.php?page=viewproblem2&cpid=648
USACO 2016 US Open Contest, Platinum
Problem 1. 262144

f[i][j]
start from i
merge [i, f[i][j]) to a single number j
if no solution, f[i][j] = -1

if we can merge [i, f[i][j-1]) to j-1 and
merge [f[i][j-1], f[f[i][j-1]][j-1]) to j-1 then
f[i][j] = f[f[i][j-1]][j-1]

http://usaco.org/index.php?page=viewproblem2&cpid=574
USACO 2015 December Contest, Gold
Problem 2. Fruit Feast

#include <bits/stdc++.h>
using namespace std;
bool f[1000100]={1};//由于题目只需要判断可否达到饱食度,故不需要开int类型
int a,b,t;
int main(){
    cin>>t>>a>>b;
    for(int i=0;i<=t;++i)
    {
        f[i + a] |= f[i];
        f[i + b] |= f[i];
    }
    for(int i=b;i<=t;++i)f[i]|=f[i-b];
    for(int i=1;i<=t;++i)f[i/2]|=f[i];// if we can reach i, then we can reach i/2
    for(int i=0;i<=t;++i)
    {
        f[i + a] |= f[i];
        f[i + b] |= f[i];
    }
    while(!f[t])--t;//从饱食度最高值向低枚举
    cout<<t<<endl;
    return 0;
}

CF467C George and Job
f[i][j]
choose j intervals in the first i elements
if we don't choose a[i]
f[i][j] = max(f[i-1][j])
if we choose a[i-k+1] .. a[i]
f[i][j] = max(f[i-k][j-1] + sum(a[i-k+1] .. a[i]))

CF706C Hard problem
lexicographical

if A is a prefix of B
the shorter is smaller.

find the first different character
the smaller character is the smaller word.

s[i] the original string i
t[i] the reversed string i

f[i][0] if we don't reverse the i-th string, what is the min cost
f[i][1] if we do reverse the i-th string, what is the min cost

f[i][0] = min(f[i][0], f[i-1][0]) iff s[i] >= s[i-1]
f[i][0] = min(f[i][0], f[i-1][1]) iff s[i] >= t[i-1]
f[i][1] = min(f[i][1], f[i-1][0] + c[i]) iff t[i] >= s[i-1]
f[i][1] = min(f[i][1], f[i-1][1] + c[i]) iff t[i] >= t[i-1]

f[i][1] = min(f[i][1], f[i-1][])

https://codeforces.com/problemsets/acmsguru/problem/99999/347
sort them directly

b
ba

bba
bab

bc
b

bbc
bcb

bool cmp(string a, string b)
{
return a + b < b + a;
}

http://main.edu.pl/en/archive/oi/19/okr
https://www.acmicpc.net/problem/8228

abcabc = abc
bcabc = bcabc

the answer must be a divisor of the length of interval.
preprocess all prime divisors of each number.

Each query is a pair of integers a and b (1 ≤ a ≤ b ≤ n),

l = the length of interval
z = the answer

b - a + 1 == l
s[a .. b]

for the answer z
it must satisfy
s[a .. b-z] == s[a+z .. b]

abcabcabcabc == s
abcabcabc == s[a+z .. b]
abcabcabc == s[a .. b-z]

for some integer t
if it satisfies
s[a .. b-t] == s[a+t .. b]
then z is a divisor of t

'abcdef'

'a'
'a' * 100 + 'b'
'a' * 10000 + 'b' * 100 + 'c'
'a' * 1000000 + 'b' * 10000 + 'c' * 100 + 'd'
'a' * 100000000 + 'b' * 1000000 + 'c' * 10000 + 'd' * 100 + 'e'
'a' * 10000000000 + 'b' * 100000000 + 'c' * 1000000 + 'd' * 10000 + 'e' * 100 + 'f'

('a' * 10000000000 + 'b' * 100000000 + 'c' * 1000000 + 'd' * 10000 + 'e' * 100 + 'f') -
('a' * 10000 + 'b' * 100 + 'c') * 1000000 = ('d' * 10000 + 'e' * 100 + 'f')

for(int i = 1;i <= n;i++)
{
    ss[i] = ss[i-1]*seed;
}

Hash:
the same strings must have the same hash values.
the different strings should have different hash values.

https://codeforces.com/topic/60786/en3

http://www.lydsy.com/JudgeOnline/problem.php?id=3097
http://www.lydsy.com/JudgeOnline/problem.php?id=3098

by Sieve, we can get primes <= n
and the least prime factor of each number.

CF611C New Year and Domino

aaa.#a.#
.#aaaaa.

.#aaa.

a.#.##

aaaaaaa.

b.bb#bb#
.#b.bbbb

b#.b..

bb#b##

........

CF711C Coloring Trees

1 1 2 -> 1 + 3 + 6 = 10
2 1 1 -> 2 + 3 + 5 = 10

f[i][j][k]
O(n * k * m * m)

first i elements
j segments
the last color is k

CF835C Star sky

CF1215B The Number of Products
5 -3 3 -1 1
1 1 -1 -1 1 1

choose a prefix product 1
choose a prefix product -1
they form an interval whose product is negative.

s[r] = a[1] * a[2] * ... * a[r]
s[l] = a[1] * a[2] * ... * a[l]

if s[r] == -1 and s[l] == 1

a[l+1] * a[l+2] * ... * a[r] = s[r] / s[l] = -1

n * (n + 1) / 2

CF1207C Gas Pipeline

f[i][0] .... (i, 1) not include the pillar from (i, 0) to (i, 1)
f[i][1] .... (i, 2) not include the pillar from (i, 0) to (i, 2)

CF1239A Ivan the Fool and the Probability Theory

CF1221D Make The Fence Great Again

#include<bits/stdc++.h>
#define REP(i,s,t) for(int i=s;i<=t;i++)
using namespace std;
const int maxn=2e5+5;
typedef long long ll;
char s[maxn];
ll f[maxn][2];
int n,a,b;
int main(){
    int T; cin>>T;
    while(T--){
        memset(f,0x3f,sizeof f);
        cin>>n>>a>>b;
        // a pipe
        // b pillar
        scanf("%s",s+1);
        f[0][0]=0;
        REP(i,1,n){
            if(s[i]=='0')
            {
                f[i][0]=min(f[i-1][0]+a+b,f[i-1][1]+a*2+b*2);
                f[i][1]=min(f[i-1][0]+a*2+b,f[i-1][1]+a+b*2);
            }
            else
            {
                f[i][1]=f[i-1][1]+a+b*2;
            }
        }
        cout<<f[n][0]+b<<'\n';
    }
    return 0;
}

CF1202B You Are Given a Decimal String...

-1 -1 -1 -1 -1 -1 -1 -1 -1 -1
3 3 3 1 1 4 2 2 2 3
2 2 2 0 0 3 1 1 1 2
[0][1][2][3][4][5][6][7][8][9] d

d[8] * 1 + d[6] * 2 = 3

0 5
0 -1 -1 -1 -1 0 -1 -1 -1 -1

from collections import*
s=input()
c=Counter((ord(y)-ord(x))%10 for x,y in zip(s,s[1:]))
for i in range(100):
    # (i//10) (i%10) counter
    a = [-1] * 10
    for j in range(1, 11):
        for k in range(j+1):
            x = ((j - k) * (i//10) + k * (i%10)) % 10
            if a[x] == -1:
                a[x] = j-1
    z = 0
    for x in c:
        if a[x] == -1:
            z = -1
            break
        else:
            z += a[x] * c[x]
    print(z)

CF1197D Yet Another Subarray Problem

#include <bits/stdc++.h>
using namespace std;
int n, m, l;
int a[300020];
int main()
{
    scanf("%d%d%d", &n, &m, &K);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
    }
    long long z = 0;
    // best interval [l, r]
    for (int i = 0; i < m; i++)
    {
        // i == l % m

        long long s = 0;
        // here the interval is [l, r)
        for (int j = i; j < n; j++)
        {
            if (j % m == i)
            {
                s = max(s, 0LL) - K;
                // if s < 0, then delete the first (j - i) elements
                // from t*m to t*m+1
                // we need decrease K more
            }
            s += a[j]
            // [i, j] == [l, r];
            z = max(z, s);
        }
    }
    printf("%lld\n", z);
    return 0;
}

https://www.luogu.com.cn/problem/P1115

include <bits/stdc++.h>

using namespace std;
int n, x, s, z;
int main() {
scanf("%d", &n);
z = -10000;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
s = max(s, 0) + x;
// if we can choose empty subarray
// s = max(s + x, 0);
z = max(z, s);
}
printf("%d\n", z);
return 0;
}

CF1181C Flag

CF1155D Beautiful Array

CF1209E1 Rotate Columns (easy version)

CF1201D Treasure Hunting

CF1152D Neko and Aki's Prank
Catalan Number

sum(f[i][j] if (i+j) %2 == 1)

1 1
1 2 2
1 3 5 5

0 0
0 1 1
0 1 2 2

0 0
0 0 0
1 1 1 1

0 0
0 0 0
0 0 1 1

1
1 1
1 2 2
1 3 5 5

0
0 1
0 1 1
0 1 2 2

0
0 0
0 0 1
0 0 1 1

1
1 2
1 3 4
1 4 8 8

P5204 [USACO19JAN]Train Tracking 2 P

P5422 [USACO19OPEN]Compound Escape P

P6275 [USACO20OPEN]Sprinklers 2: Return of the Alfalfa P

P6277 [USACO20OPEN]Circus P

https://atcoder.jp/contests/cf17-relay-open/tasks
https://atcoder.jp/contests/code-festival-2017-quala
https://atcoder.jp/contests/code-festival-2017-quala/tasks/code_festival_2017_quala_d
|x1 - x2| + |y1 - y2|

(a1, b1)
a1 = x1 + y1
b1 = x1 - y1

(a2, b2)
a2 = x2 + y2
b2 = x2 - y2

|x1 - x2| + |y1 - y2| = max(|a1 - a2|, |b1 - b2|)

|d| = max(d, -d)

for two squares, if (ai - aj) == d, color them with different colors.
for two squares, if (bi - bj) == d, color them with different colors.

for two squares, if a / d, color them with different colors.

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m, d;
    scanf("%d%d%d", &n, &m, &d);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            int x = (m + i + j) / d % 2;
            int y = (m + i - j) / d % 2;
            char c = "RGBY"[2 * x + y];
            printf("%c", c);
        }
        printf("\n");
    }
    return 0;
}

if (i1, j1) and (i2, j2), their distance is d
a1 = i1 + j1
b1 = i1 - j1
a2 = i2 + j2
b2 = i2 - j2
either abs(a1 - a2) == d || abs(b1 - b2) == d

abs(a1 - a2) == d
a1 / d % 2 is different from a2 / d % 2

https://atcoder.jp/contests/code-festival-2017-qualb

https://atcoder.jp/contests/code-festival-2017-qualb/tasks/code_festival_2017_qualb_e

  1. Remove some red balls
  2. Remove one blue ball, to do this, we set s, for the following B-1 balls, it can be either blue or red.
    i is the number of blue balls removed in step 2
  3. Remove some red balls
  4. Remove one blue ball, to do this, we set t, for the following B-i-1 balls, it can be either blue or red.
    j is the number of blue balls removed in step 4
  5. Remove the remaining balls (some red + some blue)

special case:
remove all blue balls together.
there are R+1 ways

include <bits/stdc++.h>

using namespace std;
int n, m, p = 1000000007;
long long v[100020];
long long f[100020];
long long ans;
long long C(int n, int m) {
return f[n] * v[m] % p * v[n - m] % p;
}
int main() {
v[0] = v[1] = f[0] = f[1] = 1;
for (int i = 2; i <= 100010; i++) {
v[i] = v[p % i] * (p - p / i) % p;
}
for (int i = 2; i <= 100010; i++) {
f[i] = f[i - 1] * i % p;
v[i] = v[i - 1] * v[i] % p;
}
scanf("%d%d", &n, &m);
n = the number of red
m = the number of blue
ans = n + 1;
for (int i = 1; i < m; i++) {
if (m - i > n) {
continue;
}
for (int j = 1; j <= m - i; j++) {
if ((m - i) + (m - i - j) > n) {
continue;
}
ans += C(m - 1, i - 1) * C(m - i - 1, j - 1) % p * C(n - (m - i) - (m - i - j) + 2, 2) % p;
// i-1 balls of m-1 balls are blue
// j-1 balls of m-i-1 balls are blue
// the number of remaining red balls n - (m - i) - (m - i - j)
// distribute them in to step 1 3 5
ans %= p;
}
}
cout << ans << endl;
return 0;
}

https://atcoder.jp/contests/code-festival-2017-qualb/tasks/code_festival_2017_qualb_f
a a b b c c
a ac b b c
ac ac b b
ac acb b
acb acb
acbacb

a a a a a a a a b c

aaaabaaaac
aaaaacaaab
aaaaaaaabc

https://atcoder.jp/contests/code-festival-2017-qualc

spoj ISELECT

https://www.spoj.com/problems/ISELECT/

spoj SNOWGAME

P1879 [USACO06NOV]Corn Fields G

f[i][j]
for the first i rows, the i-th row is j

j is a set, we choose in the i-th row.

i = 000111
i>>1 = 00011
& = 00011

i = = 010101
i>>1 = 001010
& = 000000

a[i] = 10111 = {0, 1, 2, 4}
s[j] = 10101 = {0, 2, 4}

(s[j] & a[i]) == s[j]

1 1 1 111 = 7
0 1 0 010 = 2
0 0 1 100 = 4

1 0
1 1
1 0

f[i][j]
前i行,第i行选了j集合中的位置
0 <= i <= n
0 <= j < 2 ** m

m = 4

0000
0001
0010
0100
0101
1000
1001
1010

10 的 二进制 是 1010 = {1, 3}
15 的 二进制 是 1111 = {0, 1, 2, 3}

m=1, the number of ways=2
m=2, the number of ways=3
m=3, the number of ways=5
m=4, the number of ways=8
m=12, the number of ways=377

3 5
4 8
5 13
6 21
7 34
8 55
9 89
10 144
11 233
12 377

P3052 [USACO12MAR]Cows in a Skyscraper G

f[i] is the best solution of subset i

for (try all set i)
{
for (try all i's subset j)
{
if (sum(j) <= w)
{
f[i] = min(f[i], f[i-j] + 1);
}
}
}

for a prefix of the permutation, we want to minimize

  1. the number of sets.
  2. the sum of the last sum.

f[i] 表示集合i的最优解
转移的话枚举i的一个子集
时间复杂度是3 ** n

P2915 [USACO08NOV]Mixed Up Cows G
f[i][j]
use the set i
the last cow is j
0 <= i < 2 ** n
0 <= j < n

from O(n!) to O(2 ** n * n ** 2)

P3067 [USACO12OPEN]Balanced Cow Subsets G

{1, 2, 3, 4, 5, 6, 7, 8}
{1, 4, 5, 8} {2, 3, 6, 7}
{1, 2, 7, 8} {3, 4, 5, 6}

3 ** (n/2)

worst case
C(n/2, n/4) ** 2

P3092 [USACO13NOV]No Change G
f[i] the maximum number of purchases of coin set i

O(2 ** k * k * log n)

find the largest i, such that a[i] <= x = find the smallest i, such that a[i] > x -

upp

{} {1} {2} {1, 2}

P1879 [USACO06NOV]Corn Fields G
P2915 [USACO08NOV]Mixed Up Cows G
P3052 [USACO12MAR]Cows in a Skyscraper G
P3092 [USACO13NOV]No Change G
P4877 [USACO14FEB]Cow Decathlon G

spoj REDRONESIA
P2167 [SDOI2009]Bill的挑战

P4544 [USACO10NOV]Buying Feed G

f[i][j] 到 位置i 携带 j 的饲料,最小花费
如果i位置没有商店,那么直接f[i][j] = f[i-1][j] + j * j
如果i位置有商店,那么直接f[i][j] = min(f[i-1][j] + j * j, f[i][j - k] + k * 单价) 在i位置买k的饲料

上限 100 个, 100 个 零一背包

100 = 1 + 2 + 4 + 8 + 16 + 32 + 37

5 * 1
5 * 2
5 * 4
5 * 8
5 * 16
5 * 32
5 * 37

P1879 [USACO06NOV]Corn Fields G

f[i][j]
前i行,第i行选了j集合中的位置
0 <= i <= n
0 <= j < 2 ** m

m = 4

0000
0001
0010
0100
0101
1000
1001
1010

10 的 二进制 是 1010 = {1, 3}
15 的 二进制 是 1111 = {0, 1, 2, 3}

if (j >> i & 1) 判断j的第i位是否是1

m 合法的状态数
3 5
4 8
5 13
6 21
7 34
8 55
9 89
10 144
11 233
12 377

P3092 [USACO13NOV]No Change G
f[i] 表示集合i的最优解

if ((i >> j & 1) == 0)
if (~ i >> j & 1)

P3052 [USACO12MAR]Cows in a Skyscraper G

https://www.luogu.com.cn/blog/feecle6418/solution-p3052

f[i] 表示i集合最少分成几组,
转移的话枚举 i 的一个子集
时间复杂度是3 ** n

j = (j - 1) & i
--j &= i;

for (int i = 0; i < 1 << n; i++)
{
for (int j = i; j > 0; j = (j - 1) & i)
{
(这里会运行 3 ** n 次)
枚举i的所有非空子集 j
if (j集合中的数字之和 <= w)
{
f[i] = min(f[i], f[i - j] + 1);
}
}
}

为什么是 3 的 n 次方?
因为对于每一位来说,只有 3 种可能,
i = 0, j = 0
i = 1, j = 0
i = 1, j = 1
一共有n位,

f[i] = pair
表示已经放了 i 集合这些物品,最少需要几组,在此基础之上最后一组之和最小。

P4877 [USACO14FEB]Cow Decathlon G

__builtin_popcount

spoj REDRONESIA

P2167 [SDOI2009]Bill的挑战
容斥原理

希望和ABCD中恰好2个匹配
[AB] 表示一定和AB匹配,和CD的关系任意

[AB] + [AC] + [AD] + [BC] + [BD] + [CD] - 3 * [ABC] - 3 * [ABD] - 3 * [ACD] - 3 * [BCD] + 6 * [ABCD]

C(-3, 0) = 1 = 1
C(-3, 1) = -2 / 1 = -3
C(-3, 2) = -3 * -4 / 2 = 6
C(-3, 3) = -3 * -4 * -5 / 6 = -10

C(n, m) = n * (n - 1) * .. * (n - m + 1) / m!

P1433,P1879,P1896,P2051,P2150,P2167,P2704,P3052,P3067,P3092,P3160,P3226,P3349,P3959,P5005

判断i的第j位是不是1
if (i >> j & 1)

(最低位是第0位)

判断i的第j位是不是0
if (~i >> j & 1)

输入一个图,求一条路径,访问每个点1次,问最短距离/问不经过重复的点是否可行,都NPC

f[i][j]
i是一个集合
0 <= i && i < (1 << n)
0 <= j < n
当前已经经过了i集合中的点,最后一个经过的点是j
显而易见(i >> j & 1) == 1

输入n个点,m条边的有向图
问有多少种方案从任意起点出发,遍历整个图,不经过重复点

double f[1];
memset(f, i, sizeof f);
printf("%d %g\n", i, f[0]);

double存储方式和int/long long不一样
结果不那么好预测
127 1.38242e+306 最大的
128 -2.93745e-306 最小的
254 -5.31401e+303

fill(f, f + n, 1e30);

f[i][j]
i是一个集合
0 <= i && i < (1 << n)
0 <= j < n
f[i][j] -> f[i | 1 << k][k]
2n * n2

直接暴力所有排列,时间复杂度
n! * n

状态压缩DP
2n * n2

P2915 [USACO08NOV]Mixed Up Cows G
f[集合][最后一个是什么]
处理排列相关的状态压缩动态规划

f[i]
i是一个集合,最小分成几组
枚举一个集合j,和i没有交集,集合j中所有牛的重量<=w

for (int i = 0; i < 1 << n; i++)
{
for (int j = 0; j < 1 << n; j++) // (想个办法枚举 n - 1 - i的子集)
{
if ((i & j) == 0)
{
cnt++;
直接做 O(4n);
优化做 O(3
n);
if (s[j] <= K)
{
f[i+j] = min(f[i+j], f[i] + 1);
}
}
}
}
cnt == 3 ** n;
3种情况

  1. i和j的第k位都是0
  2. i的第k位是1,j的第k位是0
  3. i的第k位是0,j的第k位是1

3**n 正确但可能超时的做法
https://www.luogu.com.cn/blog/feecle6418/solution-p3052
for(int i=1;i<(1<<n);i++){
for(int j=i;j;j=(j-1)&i){
if(s[j]>K)continue;
f[i]=min(f[i],f[i-j]+1);
}
}

2**n * n的做法
需要注意,我们决定的并不是如何分组,只需要决定加入顺序就可以了
另一个人看到这些牛的顺序,用贪心来决定如何分配子集
pair(到当前为止至少需要几组,在用最小组数的前提下,最后一个集合 sum最小是多少)

不可做题:给出n个数字ai
问能否把这n个数字分成两组,和相同。
ai <= 1e9
n = 100000
这没办法做

P3092 [USACO13NOV]No Change G

lower_bound(a, a + n, x) - a; 的类型是什么(指针的差类型是什么)

P2051 [AHOI2009]中国象棋
一行一行来放
状态需要记录
f[放了几行][有多少列放了0个炮][有多少列放了1个炮][有多少列放了2个炮]
最后3维只需要记2维
状态数n * m * m
转移O(1)

放0个棋子

  1. 什么都不放
    放1个棋子
  2. 0个炮->1个炮
  3. 1个炮->2个炮
    放2个棋子
  4. 0个炮->1个炮 0个炮->1个炮
  5. 0个炮->1个炮 1个炮->2个炮
  6. 1个炮->2个炮 1个炮->2个炮
    不建议记忆化搜索

n = 2
m = 2
f[1行][1列0个炮][1列1个炮] = 2
所有满足有 1列0个炮 1列1个炮 方案之和所以等于2
任意一个满足有 1列0个炮 1列1个炮 方案之和所以等于1
(显而易见,所有 1列0个炮 1列1个炮 的方案数是相同的)

第一种理解 = 第二种理解 * 方案数

f[i][j][k] = f[i行][j个1][k个2]

include

include

include

include

include

define mod 9999973

define R register

using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x10+s-'0';s=getchar();}
x
=f;
}
int n,m;
typedef long long ll;
ll ans;
ll f[108][108][108];
ll inv(ll x)
{
if (x == 1)
{
return 1;
}
return inv(mod % x) * (mod - mod / x) % mod;
}
inline ll C(ll x, ll y)
{
ll re = 1;
for (int i = 0; i < y; i++)
{
re = re * (x - i) % mod * inv(i + 1) % mod;
}
return re;
}
signed main()
{
in(n),in(m);
f[0][0][0]=1;
for(R int i=1;i<=n;i++)
{
for(R int j=0;j<=m;j++)
{
for(R int k=0;k<=m-j;k++)
{
f[i][j][k]=f[i-1][j][k];
if(k>=1)f[i][j][k]+=f[i-1][j+1][k-1]*k;
if(j>=1)f[i][j][k]+=f[i-1][j-1][k]*j;
if(k>=2)f[i][j][k]+=f[i-1][j+2][k-2]*C(k,2) % mod;
if(k>=1)f[i][j][k]+=f[i-1][j][k-1]kj % mod;
if(j>=2)f[i][j][k]+=f[i-1][j-2][k]*C(j,2) % mod;
f[i][j][k]%=mod;
}
}
}
for(R int i=0;i<=m;i++)
for(R int j=0;j<=m;j++)
{
ans += f[n][i][j] * C(m, i) % mod * C(m - i, j) % mod;
}
printf("%lld\n",(ans+mod)%mod);
}

P4783 【模板】矩阵求逆
http://acm.hdu.edu.cn/showproblem.php?pid=6410

const int mod = 1000000007
王逸松 骆可强

int
加法 减法 越界 相当于对232取模
unsigned 加减法越界,相当于对2
32取模
signed 加减法越界,是未定义行为
if (a < 0 && b < 0 && a + b > 0)
{
// 永远不会被运行到
}

乘法越界
unsigned 32位
unsigned * unsigned
计算机内部会得到64位的结果,但是只会返回低32位

加减法不区分有无符号
乘法区分有无符号的乘法

除法和余数

  1. 计算机真的算除法,没有被优化
    (unsigned, unsigned) / unsigned = unsigned ...... unsigned

(long long, long long) / long long = long long ...... longlong

long long * long long % long long
可以内联汇编

https://www.cnblogs.com/bibibi/p/9613151.html

  1. 计算机没有算除法,优化成了乘法和右移
    先计算商
    如果需要余数
    被减数 - 除数*商 = 余数

32位计算机的long long 比 64位计算机的long long慢非常多

P1879 [USACO06NOV]Corn Fields G
f[i][j]
i:行数 0 <= i && i <= n + 1
j:列的子集 0 <= j && j < 1 << m
第i行,j集合 这些列被选了
枚举第i+1行 (或者是i-1行,谁转移到i)的状态k
(j & k) == 0

j的取值很小不是2**m

1: 2
2: 3
3: 5
4: 8
5: 13
6: 21
7: 34
8: 55
9: 89
10: 144
11: 233
12: 377

直接暴力枚举j和k的转移数是377*377
可以更快吗?是可以的,需要DFS

1L << 31

i >> j & 1

2 ** 31 = 2147483648 > 2147483647

P2704 [NOI2001]炮兵阵地
f[i][j][k]
前i行
第i行是j
第i-1行是k

P5005 中国象棋 - 摆上马
https://www.luogu.com.cn/blog/user24518/solution-p5005

P2150 [NOI2015]寿司晚宴
2 3 5 7

第一个人取了 2 4 8 16
第二个人取了 3 9 27 81

{2} {3, 5, 7} + 1
{2, 5} {3, 7} + 1
{2, 7} {3, 5} + 1
{2, 5, 7} {3} + 1

{2} {3, 5} - 1
{2, 5} {3} - 1
{2} {3, 7} - 1
{2, 7} {3} - 1

{2} {3} + 1

能不能出到1000呢?

P2167 [SDOI2009]Bill的挑战
一共4个字符串,恰好匹配其中2个
ABCD

只能处理类似于 求匹配AB(CD是否匹配不知道)的方案数

方法一:
求出每个子集的答案,做一遍子集和变换

方法二:
AB + AC + AD + BC + BD + CD - 3 * ABC - 3 * ABD - 3 * ACD - 3 * BCD + 6 * ABCD

include<stdio.h>

include

include<string.h>

define C(x) __builtin_popcount(x)

using namespace std;
char s[20][60];
int c[20][20];
int tt,n,q,l,mo=1000003;
int dp(int b) {
int z = 1;
for (int i = 0; i < l; i++) {
char c = '?';
for (int j = 0; j < n; j++) {
if ((b >> j & 1) && s[j][i] != '?') {
if (c != '?' && c != s[j][i]) {
return 0;
}
c = s[j][i];
}
}
if (c == '?') {
z = z * 26 % mo;
}
}
return z;
}
int main()
{
for (int i = 0; i < 20; i++) {
c[i][0] = 1;
for (int j = 1; j <= i; j++) {
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
}
for(scanf("%d",&tt);tt--😉
{
scanf("%d%d",&n,&q);
for(int i=0;i<n;i++)
scanf("%s",s[i]);
l = strlen(s[0]);
long long ans = 0;
for (int i = 0; i < 1 << n; i++) {
if (__builtin_parity(i ^ q))
ans = (ans - (long long)c[__builtin_popcount(i)][q] * dp(i)) % mo;
else
ans = (ans + (long long)c[__builtin_popcount(i)][q] * dp(i)) % mo;
}
printf("%lld\n", ans);
}
return 0;
}

折半搜索
P4799 [CEOI2015 Day2]世界冰球锦标赛

需要用2n的时间生成一个大小为n的集合的所有子集之和
注意是2
n的时间,不是2**n * n
可以用数组和lowbit推,也可以dfs
a[0 .. n-1]
s[0 .. (1 << n) - 1]
for (int i = 0; i < n; i++)
{
s[1 << i] = a[i];
}
for (int i = 1; i < 1 << n; i++)
{
s[i] = s[i - (i & -i)] + s[i & -i];
}

map<int, vector >

P3349 [ZJOI2016]小星星
树 1 2 3 -> 图 1 2 3
树 1 2 3 -> 图 3 2 1

树 1 2 3 -> 图 1 2 1
树 1 2 3 -> 图 2 1 2

树 1 2 3 -> 图 3 2 3
树 1 2 3 -> 图 3 1 3

P3959 宝藏
https://www.luogu.com.cn/blog/dazade8/solution-p3959

P2602, P2606, P3746, P3914, P4827, P6006, P6009, P6075, P6146, P6144

P6075 [JSOI2015]子集选取

n=1    x
n=2    x**2
n=3    x**3
n      x**n

k=2    4**n

P2606 [ZJOI2010]排列计数

有根树 的 拓扑序列 的数量

n! / 每一个子树的大小

f[x] = f[2 * x] * f[2 * x + 1] * (s[x]! / s[x] / s[2 * x]! / s[2 * x + 1]!)

p <= 1e9
n <= 1e9
输入 n ,输出 n! % p
没有特别简单的做法

分块打表,打表,每隔 1000000 打一个数字

f[i][j]

从i节点到i子树中的某个节点,路径长度%3 == j的方案数。

有f[x][0]个0
有f[x][1]个1
有f[x][2]个2
从中选取2个数字,和为0的方案数是多少?
(减去 选的2个数字在同一个子树 的情况)

P3914 染色计数
f[i][j]
子树i的情况,点i染成了颜色j,的方案数

g[i] 子树i的情况,所有染色方案之和

枚举i的孩子x,
f[i][j] *= sum(k != j, f[x][k])
显然不能枚举求和
f[i][j] *= sum(f[x][k]) - f[x][j]

P3478 [POI2008]STA-Station 一次方的特殊情况
P4827 [国家集训队] Crash 的文明世界
spoj TREESUM
hdu 4625 JZPTREE

S是距离的集合

已知
a0 = S中所有数字0次方的和
a1 = S中所有数字1次方的和
a2 = S中所有数字2次方的和
...
ak = S中所有数字k次方的和

b0 = S中所有数字+1之后0次方的和
b1 = S中所有数字+1之后1次方的和
b2 = S中所有数字+1之后2次方的和
...
bk = S中所有数字+1之后k次方的和

b0 = a0
b1 = a1 + a0
b2 = a2 + 2 a1 + a0
b3 = a3 + 3 a2 + 3 a1 + a0

bk = C(k,k) ak + C(k,k-1) a(k-1) + ... + C(k,0) a0

O(n k^2)

已知
a0 = S中所有数字x C(x, 0)的和
a1 = S中所有数字x C(x, 1)的和
a2 = S中所有数字x C(x, 2)的和
...
ak = S中所有数字x C(x, k)的和

b0 = S中所有数字x C(x+1, 0)的和
b1 = S中所有数字x C(x+1, 1)的和
b2 = S中所有数字x C(x+1, 2)的和
...
bk = S中所有数字x C(x+1, k)的和

b0 = a0
b1 = a1 + a0
b2 = a2 + a1
b3 = a3 + a2

bk = ak + a(k-1)

对于每个点x,求的是所有距离d C(d, 0..k) 并不是 d**k

第二类Stirling数
S(k, j) 把k个不同元素分成j个非空集合的方案数

x**k = sum(1 <= j <= k, S(k, j) * j! * C(x, j))
左边是 k 个不同球,放入 x 个不同的盒子的方案数
右边换一种方法数
假设放入之后,有j个盒子有球
首先 k 个不同的球分成 j 组 S(k, j)
从 x 个盒子中选出 j 个盒子 C(x, j)
把分成的 j 组和 j 个盒子 配对 j!

树形DP

P1352 没有上司的舞会
P1122 最大子树和
P4084 [USACO17DEC]Barn Painting G

P1273,P1552,P4383,P6142,P6147,P4084,P4827,P5203,P5243,P5853,P6008

P1352 没有上司的舞会
f[x] 不选 x, x 子树的最大值是多少
g[x] 选 x, x 子树的最大值是多少

P1122 最大子树和
f[x] 表示选择第 x 个点,在 x 子树中最大权值和是多少

P2796 Facer的程序
f[x] 表示选择第 x 个点,只考虑 x 子树中的点,方案数是多少
f[x] = 枚举所有孩子i (f[i]+1)的连乘积

P1273 有线电视网
树上背包
f[i][j] i子树花j的钱,最多满足多少人
f[i][j] i子树满足j个人,最多赚多少钱(负数表示最小亏多少)
在第i个点消耗了多少时间:有多少对点以i为LCA
所以总时间不会超过 点对 个数

f[i][j]
i 子树中选 j 个节点

P1552 [APIO2012]派遣
启发式合并

问区间第 k 小是谁?
问数字 x 是第几小?
问最小的 k 个和是多少?
问选择和不超过 s 最多选择几个数字?

P4383 [八省联考2018]林克卡特树
wqs二分(凸优化)
选择k+1条路径
把他们接在一起
每一条路径可以是单独一个点
路径和路径之间不能有公共点

原本要求k+1个
现在不要求个数
但是每选一个路径会获得M的额外收益(M可能是负数)
问最大收益,求最大收益的同时,需要求解选择了几个路径。

P6147 [USACO20FEB]Delegation G
每个点自己做匹配

P6142 [USACO20FEB]Delegation P
二分,每个点用 set 匹配,有非常多的特殊情况。

DFS,每个点只考虑孩子,希望把孩子尽可能的匹配
剩下一条最长的链传给父亲节点
每一个点有一个集合,要求把集合中的数字配对,使得每对和都>=M

有一个列表
希望删去其中一个数字
使得剩下的数字配对之后>=M
问删去的数字最大是多少?

例子
M=10
[2, 3, 7] 应该 3 和 7 匹配,返回 2
[5, 10] 是否应该匹配呢?如果是根节点,5和10匹配返回0,如果不是根节点,10自己满足条件,返回5

int dfs(int x, int y) {
multiset b;
for (int i = 0; i < a[x].size(); i++) {
if (a[x][i] != y) {
int l = dfs(a[x][i], x);
b.insert(l + 1);
if (!flag)
{
return 0;
}
}
}
int only = 0;
while (b.size() > 1)
{
int u = *b.begin(); 找到集合中最小的数字
b.erase(b.begin()); 删去最小的数字
if (u < M)
{
multiset::iterator it = b.lower_bound(M - u); 问集合中哪个数字和u匹配后可以>=M 选最小的匹配后>=M
if (it == b.end()) 如果这样的数字不存在
{
if (only == 0) 那么可能会剩下他
{
only = u; // 剩下他
continue;
}
flag = false; // 如果剩下了2个无法匹配的数字,那就无解
return 0;
}
if (b.size() == 1 && *it >= M) // 如果剩下1个 <M 剩下1个 >=M 的 情况比较复杂,返回哪个都不对
{
return *b.begin();
}
b.erase(it); // 删去和 u 匹配的数字
}
}
if (b.size() == 0) // 如果最后集合空了,返回可能存在的only
{
return only;
}
else // 如果还剩下一个
{
if (only > 0) // only 也有,剩下2个,无解
{
flag = false;
return 0;
}
return *b.begin(); // 返回剩下的一个
}
}
bool ok() {
flag = true;
int res = dfs(1, 0);
return flag && (res >= M || res == 0);
}

SP1435 PT07X - Vertex Cover
树上 最小点覆盖

SP666 VOCV - Con-Junctions
树上 最小点覆盖方案数

P4084 [USACO17DEC]Barn Painting G
树上简单动态规划
f[x][0/1/2] 表示第x个点染0/1/2颜色,x子树中有多少个方案

P4827 [国家集训队] Crash 的文明世界
斯特灵数

P5203 [USACO19JAN]Exercise Route P
树上有多少对路径边相交

P5243 [USACO19FEB]Moorio Kart P
简单背包

P5853 [USACO19DEC]Tree Depth P
笛卡尔树

P6008 [USACO20JAN]Cave Paintings P
树上dp,计数

SP1480 PT07D - Let us count 1 2 3
树的计数

AT2293 [AGC009D] Uninity

rand() << 15 | rand()

long long n;
printf("%lld\n", n);

freopen("game.in ", "r", stdin);
freopen("game.out", "w", stdout);

y1 = a x1^2 + b x1
y2 = a x2^2 + b x2
y3 = a x3^2 + b x3

y1 x1^2 x1
y2 x2^2 x2
y3 x3^2 x3

树形DP

简介

树形DP是所有DP中较为简单的一种,因为树形DP的状态一定是每个节点若干状态,所以很容易根据点数猜出每个点的状态个数,进一步猜出状态的表示。

常见技巧

在比较古老的书上有一种把多叉树转成二叉树的方法,左孩子右兄弟,用以解决一类状态合并中只能两个点合并的题目,不过目前来看这种方法用处不大。

第一类经典的题目如BLACKCOM,看起来有O(n2)O(n^2)个状态,转移是O(n)O(n)但是总复杂度O(n2)O(n^2)

第二类经典的题目如hdu4625,组合数的递推(有22项)比次幂的递推(有k+1k+1项)简单很多,可以将转移的时间复杂度从O(k)O(k)优化到O(1)O(1)

第三类经典的题目如bzoj2152,每条路径在LCA的位置被统计到,特点是每个点的状态数必须非常有限,比如如果是统计路径长度小于等于kk或者是路径长度为质数的,都是用点分治比较好。

参考题目

Codechef BLACKCOM
对于每个大小,求出最多的黑色点的个数,和最少黑色点的个数。

hdu 4625
根据题目可以感觉到至少O(nk)O(nk)个状态。
通过次幂和组合数的关系,将维护x0,x1,,xkx^0, x^1, \ldots, x^k
转化为维护(x0),(x1),,(xk)\binom{x}{0}, \binom{x}{1}, \ldots, \binom{x}{k}

bzoj 2152
聪聪可可
这个题目有一个O(nk)O(nk)的动态规划的做法,即每条路径在LCA的位置被统计到。
网上许多题解是用点分治做的,但是这个题并不需要点分治。

区间DP

区间DP是一类比较简单的DP,因为DP状态一定是一个区间

循环顺序

区间DP有三种循环顺序

枚举区间长度 l 从小到大 
    枚举起点 i (任意顺序)
        j = i + l - 1
        for (int k = i; k < j; k++)
        {
            f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + 合并之后第i堆到第j堆的大小s[j]-s[i-1] )
        }

枚举起点 i 从大到小
    枚举终点 j 从小到大

枚举终点 j 从小到大
    枚举起点 i 从大到小

枚举循环要保证 计算[i, j]时,所有被[i, j]包含的区间都已经计算了

相关题目

P1880 [NOI1995] 石子合并
P1063 [NOIP2006 提高组] 能量项链
P1220 关路灯
P3205 [HNOI2010]合唱队
P3146 [USACO16OPEN]248 G
P4170 [CQOI2007]涂色
P7414 [USACO21FEB] Modern Art 3 G

P1880 [NOI1995] 石子合并

f[i][j]
从 第 i 堆 合并到 第 j 堆 最大/最小 得分
最后一次是怎么合并的
[i,k] 合并 [k+1,j]

P1220 关路灯

5 3
2 10
3 20
5 20
6 30
8 10

5->6 70 * 1
6->3 40 * 3
3->2 20 * 1
2->8 10 * 6

f[i][j] 表示关 i 到 j 的灯 耗电最少
发现还需要记录,最后停在 i 还是停在 j

P3205 [HNOI2010]合唱队

f[i][j] 如果 第i个 人到 第j个人 如果理想队列只有这些人,方案数是多少

P4170 [CQOI2007]涂色

P7414 [USACO21FEB] Modern Art 3 G
f[i][j] i到j最少染几次
转移,区间可以拼起来
f[i][j] = min(f[i][j], f[i][k]+f[k+1][j])
if (s[i] == s[j])
{
f[i][j] = f[i][j - 1];
}

P3146 [USACO16OPEN]248 G

f[i][j] 表示 i 到 j 合并出的数字最大是什么
可以发现,如果能合并出来,那么合并出来的数字结果是唯一的

P3147 [USACO16OPEN]262144 P
f[i][j]
从左端点是i,合并出数字j,右端点是唯一的,合并不出记 0
如果 a[i] == j
那么不需要合并 f[i][j] = i + 1 // 左闭右开
f[i][j] = f[f[i][j-1]][j-1]

P5851 [USACO19DEC]Greedy Pie Eaters P
对于所有没输入的区间,都假设有一个体重为0的牛吃
这样可以要求,最终求出的序列,每个牛只能吃一个Pie,最后一定会选出n个牛

f[i][j] 只考虑第i个Pie到第j个Pie 的最优解 ,这个最优解,不能包含吃[i, j]范围外的牛
那么这个最优解一定恰好包含 j-i+1 个牛
考虑这个区间内,最后一个被吃的是哪个Pie,有 j-i+1 种情况
假设是k

f[i][j] = max(f[i][j], f[i][k-1] + f[k+1][j] + (体重最大 i <= 左端点 <= k <= 右端点 <= j 的牛))

#include <bits/stdc++.h>
using namespace std;
int n, m, f[302][302];
int a[302][302][302];
int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        int w, l, r;
        cin >> w >> l >> r; 
        for (int k = l; k <= r; k++)
        {
            a[k][l][r] = w;
        }
    }
    for (int k = 1; k <= n; k++)
    {
        for (int i = k; i > 0; i--)
        {
            for (int j = k; j <= n; j++)
            {
                a[k][i][j] = max(a[k][i][j], max(a[k][i+1][j], a[k][i][j-1]));
            }
        }
    }
    for (int i = n; i > 0; i--)
    {
        for (int j = i; j <= n; j++)
        {
            for (int k = i; k <= j; k++)
            {
                f[i][j] = max(f[i][j], f[i][k-1] + f[k+1][j] + a[k][i][j]);
            }
        }
    }
    cout << f[1][n] << endl;
    return 0;
}

abc252_g

https://atcoder.jp/contests/abc252/tasks/abc252_g

DAG上的DP

有向无环图上的动态规划

可以先拓扑排序再动态规划,也可以直接用记忆化搜索实现

P4017 最大食物链计数

Luogu P4017

拓扑排序动态规划
输入有向无环图(DAG)问有多少条链。

P4316 绿豆蛙的归宿

拓扑排序动态规划
f[4] = 0
f[3] = f[4] + 4 = 4
f[2] = f[3] + 3 = 7
f[1] = ((f[2] + 1) + (f[3] + 2)) / 2 = 7

P1434 [SHOI2002]滑雪

记忆化搜索
不容易决定转移的顺序
已知当前状态,可以知道哪些状态转移到自己。
不一定每个状态都对最终结果有贡献,用记忆化搜索搜到的状态一定是有贡献的

递推

需要知道转移的顺序

P1434 [SHOI2002]滑雪

https://www.luogu.com.cn/problem/P1434

f[i][j] = max(f[i+-1][j+-1], a[i][j] > a[i+-1][j+-1]) + 1..

在 有向无环图 上
如果希望决定动态规划的顺序,需要拓扑排序
拓扑排序2种写法

  1. BFS
  2. DFS

(少见/很难) 不是所有的状态都用得到
记忆化搜索可以不计算不需要的状态

P1464 Function

https://www.luogu.com.cn/problem/P1464
记忆化搜索

Luogu P1807

Luogu P1807
输入有向无环图(DAG)问最长链多长。

bzoj 3036

bzoj 3036
输入有向无环图,进行概率DP。

P1807 最长路

拓扑排序 最长路

P1113 杂务

拓扑排序就是1到n,求最长路
f[1] = 0 + 5
f[2] = max(f[1]) + 2 = 7
f[3] = max(f[2]) + 3 = 10
f[4] = max(f[1]) + 6 = 11
f[5] = max(f[2], f[4]) + 1 = 12
f[6] = max(f[2], f[4]) + 8 = 19
f[7] = max(f[3], f[5], f[6]) + 4 = 23;

P1137 旅行计划

拓扑排序,动态规划

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y;
vector<int> a[100020];
int f[100020]; // f[x] > 0
int F(int x) {
    if (f[x])
    {
        return f[x];
    }
    for (int i = 0; i < a[x].size(); i++) {
        f[x] = max(f[x], F(a[x][i]));
    }
    f[x]++;
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cin >> x >> y;
        a[y].push_back(x);
    }
    for (int i = 1; i <= n; i++) {
        cout << F(i) << endl;
    }
    return 0;
}

背包

背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。

基本思路

f[i][j]表示前i个物品中取j的重量,的最大价值。
其中第一位可以用滚动数组优化。

零一背包

每个物品只能取或不取。
对于一个重量为x价值为y的物品,转移方程为
f[i][j] = max(f[i][j], f[i-1][j-x]+y);
然后i维可以用滚动数组,或者是倒序循环优化掉。
比如采药

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, f[1020];
int main() {
	scanf("%d%d", &m, &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%d", &x, &y);
		for (int j = m; j >= x; j--) {
			f[j] = max(f[j], f[j - x] + y);
		}
	}
	printf("%d\n", f[m]);
	return 0;
}

完全背包

每个物品可以取任意多次
对于一个重量为x价值为y的物品,转移方程为
f[i][j] = max(f[i][j], f[i][j-x]+y);
然后i维可以用滚动数组,或者是正序循环优化掉。
比如疯狂的采药

#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, f[100020];
int main() {
	scanf("%d%d", &m, &n);
	for (int i = 0; i < n; i++) {
		scanf("%d%d", &x, &y);
		for (int j = x; j <= m; j++) {
			f[j] = max(f[j], f[j - x] + y);
		}
	}
	printf("%d\n", f[m]);
	return 0;
}

多重背包

每个物品可以选的次数大于11,但上限不是无限大,而要求小于等于cc
一个显而易见的暴力做法是直接拆成cc个物品做零一背包。
主要有两种做法,二进制拆分,和单调队列。
第一种做法是不拆成大小为11的物品了,拆成大小为
1,2,4,8,,c2k+11, 2, 4, 8, \ldots, c - 2^k + 1
的物品,其中kk是满足c2k+1>0c - 2^k + 1 > 0最大的kk

另一种是单调队列,相当于f[i] = max(f[i - j * x] + j * y)
其中j满足0 <= j <= c
如果把数组的每x个取出的话,相当于是区间最值。

背包统计方案数

一些情况下,物品并没有价值,而是希望计算方案数。
对于零一背包和完全背包,几乎一样。
对于有限背包,就不能拆成零一背包来做了。
而需要下面这个题目的技巧
51nod 1597

一个经典的例子是通过完全背包计算Partition numbers

#include <bits/stdc++.h>
using namespace std;
int n, p = 1000000007;
int f[100020];
int main() {
	cin >> n;
	f[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++) {
			f[j] = (f[j] + f[j - i]) % p;
		}
	}
	cout << f[n] << endl;
	return 0;
}

时间复杂度是O(n2)O(n^2)的,虽然有利用五边形数公式优化到O(nn)O(n\sqrt{n})的做法。

bitset

一些背包题,如果只关注每个值能否得到,或者是得到每个值的方案数的奇偶性。
可以使用bitset优化。

只关注每个值能否得到
atcoder agc020_c
需要知道每个值的方案数的奇偶性的题目:
bzoj 3687

参考题目

Luogu P1048
采药,01背包

Luogu P1616
疯狂的采药,完全背包

Luogu P1164
小A点菜,01背包,计数版本

Luogu P1832
把n拆分成若干个质数,可以重复。完全背包,计数版本

Luogu P1734
最大约数和,01背包

Luogu P2871
手链Charm Bracelet,01背包

Luogo P2639
Bessie的体重问题,01背包,只需要知道每个重量能否达到。

Luogu P5020
货币系统,完全背包,只需要知道每个金额能否被表示出/被表示出的次数是否大于等于2。

Luogu P1510
精卫填海,01背包。

Luogu P1855
榨取kkksc03,二维01背包。

Luogu P1910
二维01背包。

Luogu P1757
分组背包

Luogu P2563
把n拆分成若干个质数,可以重复。完全背包,计数版本

Luogu P2918
完全背包

Luogu P2725
记录可以到达哪些价值,多记录一维质数个数。

Luogu P1284
二维01背包

背包问题 knapsack problem

零一背包 完全背包 有限背包

统计方案数 求最优解

背包

  1. 完全背包

  2. infinite

  3. 01背包

  4. 0 1

  5. 混合背包

  6. limited

  7. 最优化

  8. optimization

  9. 计数

  10. counting

https://www.luogu.com.cn/problem/P1048
P1048 采药

经典错误做法:
先选 价值高的
先选 体积小的
先选 价值/体积 大的
先选 价值-体积 大的

f[i][j] 表示 前i个物品,j的体积,最大价值是多少
f[i][j] first i items, use j volume, what is the max value?
f[i][j] = max(f[i-1][j], f[i-1][j-x[i]] + y[i])

f[i] = max(f[i], f[i - x] + y)
零一背包 倒序 (每个物品至多1次)
完全背包 正序 (每个物品任意多次)

本题中不需要用完所有的体积
如果要求必须用完所有的体积
那么 把 f 清成负无穷,f[0] = 0
memset(f, 0xc0, sizeof f);
f[0] = 0

P1616 疯狂的采药

P1164 小A点菜

include <bits/stdc++.h>

using namespace std;
int n, m, x, y, f[100020];
int main() {
scanf("%d%d", &n, &m);
f[0] = 1;
for (int i = 0; i < n; i++) {
scanf("%d", &x);
for (int j = m; j >= x; j--) {
f[j] += f[j - x];
}
}
printf("%d\n", f[m]);
return 0;
}

if infinite times
change for (int j = x; j <= m; j++)

Fibonacci Number
4 = 1 + 1 + 1 + 1
4 = 1 + 1 + 2
4 = 1 + 2 + 1
4 = 2 + 1 + 1
4 = 2 + 2

https://en.wikipedia.org/wiki/Partition_(number_theory)

memset(f, 0, sizeof f);
f[0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
f[j] += f[j - i];
}
}

P2871 [USACO07DEC]Charm Bracelet S
最优化 零一背包

P2639 [USACO09OCT]Bessie's Weight Problem G
只关注方案数是否为0的零一背包,可以用 bitset 优化

int f[100020];
int main() {
f[0] = true;
scanf("%d%d", &m, &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
for (int i = 100000; i >= x; i--)
{
f[i] |= f[i - x];
}
}

include <bits/stdc++.h>

using namespace std;
int n, m, x;
bitset<100000> f;
int main() {
f[0] = true;
scanf("%d%d", &m, &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
f |= f << x;
}
for (int i = m; i >= 0; i--) {
if (f[i]) {
printf("%d\n", i);
break;
}
}
return 0;
}

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

f[m] |= f[m - x]
f[m-1] |= f[m-x-1]
...

f[x] |= f[0]

练习题

P1048 采药
最优化 01背包

P1616 疯狂的采药
最优化 完全背包

P1164 小A点菜
计数 01背包

P1832 A+B Problem(再升级)
计数 完全背包

P1734 最大约数和
最优化 01背包

P2871 [USACO07DEC]Charm Bracelet S
最优化 01背包

P2639 [USACO09OCT]Bessie Weight Problem G
计数 01背包 只关注是否可行,可以bitset优化

P5020 货币系统
计数 完全背包 只关注是否可行

P1510 精卫填海
最优化 01背包

P2563 [AHOI2001]质数和分解
计数 完全背包

P2918 [USACO08NOV]Buying Hay S
最优化 完全背包

P1853 投资的最大效益
最优化 完全背包

P1757 通天之分组背包
最优化 01背包 分组背包

P2663 越越的组队
最优化 01背包 二维(需要记录个数)

P1855 榨取kkksc03
最优化 01背包 二维

P4377 [USACO18OPEN]Talent Show G
二分,然后背包

NP-complete

时间复杂度和 背包大小有关

2种题目

  1. 计数

计数 并且 完全背包 时,是否考虑物品顺序
AB 和 BA 是否相同
如果相同
先枚举物品,再枚举体积
如果不同
先枚举体积,再枚举物品

  1. 最优化

最优化时,是否要求恰好用完所有体积?
由动态规划的初始值决定
如果初始f[i]都设为0
相当于初始可以浪费一些体积
最后回答f[m]

如果初始只有f[0]=0
其他的f[i]都是-infinity
相当于是最后体积必须恰好是m

根据个数不同

  1. 01背包
  2. 完全背包
  3. 混合背包

分组背包

{1, 2, 4, 8, 16, 32, 64}
{1, 3, 9, 27, 81}
{1, 5, 25}
{1, 7, 49}

暴力动态规划

f[i][j] 前i个物品用了j的体积

混合背包
最优化 二进制拆分,单调队列

用背包计算 partition number 拆分数

bitset 优化 可行性 01背包

10
5 5
6 8

对于背包问题,几乎所有贪心都是错误的,常见错误做法

  1. 优先选 价值 大的
  2. 优先选 体积 小的
  3. 优先选 价值/体积 比例大的

输入n个数字,从中选取若干个数字,凑出m。
很困难

n <= 40, m <= 1e18 可以做
n * m <= 1e7 可以做

背包问题添加物品的顺序,不会影响答案。
有的题目需要利用这个性质。

12 = 3,4

P1048 采药
最优化 01背包

P1616 疯狂的采药
最优化 完全背包

P1164 小A点菜
计数 01背包

P1832 A+B Problem(再升级)
计数 完全背包

P1734 最大约数和
最优化 01背包

P2871 [USACO07DEC]Charm Bracelet S
最优化 01背包

P2639 [USACO09OCT]Bessie's Weight Problem G
计数 01背包 只关注是否可行,可以bitset优化

f[2] |= f[0]
f[3] |= f[1]
f[4] |= f[2]
f[5] |= f[3]
f[6] |= f[4]
f[7] |= f[5]

f = 0000110101
f << 2 = 0011010100

如果是完全背包,无法使用bitset优化

P5020 货币系统
计数 完全背包 只关注是否可行

https://henryhuang.blog.luogu.org/solution-p5020
另一种思路
直接对所有面值 做背包

  1. 记录 min(方案数, 2)
  2. 记录每个金额至多用多少个面值,如果对一个面值,可以用多张表示出自己,那么自己就是没必要的

P1510 精卫填海
最优化 01背包

2种做法
对于每个体积,算出最小的体力
对于每个体力,算出最大的体积

P2563 [AHOI2001]质数和分解
计数 完全背包

P2918 [USACO08NOV]Buying Hay S
最优化 完全背包

P1853 投资的最大效益
最优化 完全背包

P1757 通天之分组背包
最优化 01背包 分组背包

P2663 越越的组队
最优化 01背包 二维(需要记录个数)

P1855 榨取kkksc03
最优化 01背包 二维

P4377 [USACO18OPEN]Talent Show G
二分,然后背包

2种做法
对于每个重量,算出最大的才艺值,超时
对于每个才艺值,算出最小的重量,错误

3 3
2 1
1 1
1 1000

正确做法 二分 最终答案 A
每个牛的权值变为 t[i] - A * w[i]
能否选一个子集,总重量至少W
权值之和非负

P4544 [USACO10NOV]Buying Feed G

100 = 1 + 2 + 4 + 8 + 16 + 32 + 37

spoj WEIGHT3

spoj WEIGHT1

退背包

计数

最优化(分治)

并查集

简介

维护nn个元素,刚开始每个元素自己一个集合,支持两个操作。

其他支持:

不带路径压缩的查找函数

int find(int x) {
    if (f[x] != x) {
        return find(f[x]);
    }
    return f[x];
}

带路径压缩的查找函数

int find(int x) {
    if (f[x] != x) {
        f[x] = find(f[x]);
    }
    return f[x];
}

合并两个元素所在的集合

void merge(int x, int y) {
    x = find(x);
    y = find(y);
    if (x != y) {
        f[x] = y;
    }
}

时间复杂度

直接实现的话,时间复杂度最坏可以到O(n)O(n)
两个常见优化,启发式合并,路径压缩。

实现其中任意一个时间复杂度变为O(logn)O(\log n)
实现其中两个,时间复杂度变为O(α(n))O(\alpha(n)),其中α(n)\alpha(n)阿克曼函数的反函数,可以认为非常小。

多数情况下为了简单,都实现路径压缩(只需要一句赋值)而不实现启发式合并(需要记录大小)

在某些题目中由于会爆栈,需要使用非递归的find函数。

可持久化与撤销

如果希望支持可持久化(访问历史版本)和撤销最后若干次操作,
那么不能使用路径压缩优化,只使用启发式合并。

对于可持久化,需要使用可持久化数组。

对于撤销最后若干次操作,需要记下每次合并操作,修改了哪个f[x]

参考题目

Luogu P3367
并查集

Luogu P1551
并查集,因为所有边是先给定的,所以也可以DFS/BFS

Luogu P1536
并查集,数连通块,也可以DFS/BFS

Luogu P1525
排序,用并查集判断二分图

Luogu P2024
拆3个点,并查集

Luogu P1196
并查集,维护信息

Luogu P2256
map把字符串映射为数字,并查集。

Luogu P1955
并查集,用map当数组,不初始化。

并查集
Disjoint-set
Union-Find Set
Union-Find Forest

并查集:
https://codeforces.com/problemset/problem/1249/B1
https://codeforces.com/problemset/problem/1249/B2
大家传递书互相看,问每个人能看到几本书
交换关系一定是一个环,环长就是答案

https://codeforces.com/problemset/problem/217/A
输入n个点,如果两个点x相同,或者y相同,那么他们属于同一个区域
问一共有多少个区域

https://www.luogu.com.cn/problem/P1551
并查集
这类先输入所有边,再询问的题目
也可以用DFS

P3144 [USACO16OPEN]Closing the Farm S
P6121 [USACO16OPEN]Closing the Farm G
P6111 [USACO18JAN]MooTube S
P4185 [USACO18JAN]MooTube G
P5836 [USACO19DEC]Milk Visits S
P6004 [USACO20JAN]Wormhole Sort S
P5423 [USACO19OPEN]Valleys P
P3101 [USACO14JAN]Ski Course Rating G
P4269 [USACO18FEB]Snow Boots G

P1196 [NOI2002]银河英雄传说
P1333 瑞瑞的木棍
P1455 搭配购买
P1525 关押罪犯
P1536 村村通
P1551 亲戚
P1955 [NOI2015]程序自动分析
P2024 [NOI2001]食物链
P2170 选学霸
P2256 一中校运会之百米跑
P2391 白雪皑皑
P3367 【模板】并查集
P3631 [APIO2011]方格染色

  1. 初始化 Initialization
    int f[1000020];
    for (int i = 0; i <= n; i++)
    {
    f[i] = i;
    // The easiest way
    // 比较简单

    f[i] = 0;
    // sometimes we use 0 for root
    // 有时候也这样实现
    }

  2. 查找 Find

int F(int x)
{
// if (f[x] == x)
// {
// return x;
// }
// return F(f[x]);
return f[x] == x ? x : F(f[x]);
return f[x] == x ? x : f[x] = F(f[x]);
}

int F(int x)
// 查找根节点
// find the root
{
if (f[x] == x)
// if (f[x] == x || f[x] == 0)
{
return x;
// x is the root
}
return F(f[x]);
// recursion
}

int F(int x)
{
return f[x] != x ? f[x] = F(f[x]) : x;
// return f[x] == x ? x : f[x] = F(f[x]);
// change f[x] to speed up next finding
}
在C++没有区别
This two lines are the same in C++

在C语言中只有第一种可以编译过
In C, only the first one is correct, the second one is CE

如何解决超时的问题? (一条长链)
How to solve TLE? (A very long chain)

  1. compress the path
  2. 压缩路径
  3. merge by size or depth
  4. 启发式合并/按秩合并
  5. randomly merge
  6. 随机合并

只用1. 或者 只用2.
时间复杂度是 log n

  1. 和 2. 同时使用,时间复杂度 O(alpha(n))

  2. 合并 Union
    // randomly merge
    void U(int x, int y)
    {
    x = F(x);
    // find the root of the set containing x
    // 找到x所在集合的根节点
    y = F(y);
    // find the root of the set containing y
    // 找到y所在集合的根节点

    if (rand() & 1)
    {
    f[x] = y;
    // merge x to y
    // 把 x 合并到 y 上
    }
    else
    {
    f[y] = x;
    // merge y to x
    // 把 y 合并到 x 上
    }
    }

C++和C
? 三目运算符
= 赋值
优不同先级
C中 两个优先级相同
return (f[x] == x ? x : f[x]) = F(f[x]);
编译失败

C++
return f[x] == x ? x : (f[x] = F(f[x]));

C++ 可以写 (a ? b : c) = 2
C语言不可以 a + 1 = 2

启发式合并/按秩合并
把小的合并到大的上面

按点数,把小的合并到大的,启发式合并
按深度,把小的合并到大的,按秩合并
随机合并

P3367 【模板】并查集
https://www.luogu.com.cn/problem/P3367

P1551 亲戚
https://www.luogu.com.cn/problem/P1551
2种做法

  1. 并查集,读入一条边合并一条边 O(m \alpha(n))
  2. DFS/BFS,读入之后存边,最后一起DFS/BFS O(n + m)
    理论上第二种更快,但是需要存边
    第一种更好写

Tips:

  1. if all edges are given before queries, then BFS/DFS is the same union find set

BFS/DFS is faster. O(n + m)
union find set. O(m * alpha(n))
We do not need to record the edges.

http://poj.org/problem?id=2395
P1547 [USACO05MAR]Out of Hay S

P1955 [NOI2015]程序自动分析
https://www.luogu.com.cn/problem/P1955

多组数据
map<int, int> 当做数组来用
通过使用函数避免flag变量

爆栈

C++ 堆 栈
局部变量 栈
全局变量 堆

栈不止存局部变量,还存递归的其他信息

树:
连通
任意两点之间简单路径唯一
再连接任意两个点都会形成环
边数 = 点数 - 1

最小生成树:
2个性质

  1. 任意一个环上最大的边一定不在最小生成树上 -> Kruskal
  2. 任意一个割上最小的边一定在最小生成树上 -> Prim

时间复杂度
Kruskal 排序 并查集 O(m log m)
Prim 堆优化 O((n + m) log n)
Prim 暴力 O(n^2)

一般情况下 用 Kruskal 最好
特殊情况,比如完全图

MST:

  1. minimize the sum of edges
  2. minimize the longest edge
  3. minimize the second longest edge
  4. minimize the third longest edge

Kruskal:

  1. sort all edges by length
  2. for each edge, try to merge them

Prim:

https://www.luogu.com.cn/problem/P4643
P4643 [国家集训队]阿狸和桃子的游戏
把边权转换到点权上,贪心

https://www.luogu.com.cn/problem/P2916
P2916 [USACO08NOV]Cheering up the Cow G
把点权转换到边权上,做最小生成树

http://poj.org/problem?id=3657
P2898 [USACO08JAN]Haybale Guessing G

min(a[1], a[2], ..., a[10]) = 7
min(a[5], a[6], ..., a[19]) = 7
min(a[3], a[4], ..., a[12]) = 8

1 3 8
5 6 9

2 5 7

8 8 8 7 9 9

1 6 7
4 9 7

there must be at least 1 unoccupied cell a[i] (4<=i<=6)
(max_left <= i <= min_right)
(because 7 can only occur once.)

(fill from min_left to max_right, these cells are occupied.)
from 1 to 9, a[i] >= 7

map<int, vector<pair<int, int> > > g;

g[min].push_back(make_pair(left, right))

http://www.usaco.org/index.php?page=viewproblem2&cpid=644
http://www.usaco.org/index.php?page=viewproblem2&cpid=646
P3144 [USACO16OPEN]Closing the Farm S
P6121 [USACO16OPEN]Closing the Farm G

原题只问是否全连通,可以改成问有多少个连通块。
离线 处理

一个一个删除是做不到的
可以倒序考虑,一个一个加入
所有类型删除都可以这样考虑
如果只有删除操作,可以考虑倒序变成加入操作

因为加入的话,连通块个数至多+1,减少多少不一定。

删除一个点很困难
连通块可能减少1个
可能增加很多个

加入一个点很简单
连通块可能增加1个
可能减少很多个

如果只有删除,并且可以离线,那么相当于只有加入。

时间复杂度 O(m log n + n)

solve this problem offline
We can't delete, but we can add them one by one

delete a vertex
the number of components -1
the number of components +?

add a vertex
the number of components +1
the number of components -?

http://www.usaco.org/index.php?page=viewproblem2&cpid=788
http://www.usaco.org/index.php?page=viewproblem2&cpid=789
P6111 [USACO18JAN]MooTube S
P4185 [USACO18JAN]MooTube G
离线 解决这个问题

把所有边排序,询问排序
把所有边从大到小依次加入,中间询问每个连通块的大小

实现:

  1. 一起排序

solve this problem offline
sort all edges and queries by relevance

add edges from larger relevance to smaller relevance
each time we query the number of vertices in the component.

http://www.usaco.org/index.php?page=viewproblem2&cpid=968
P5836 [USACO19DEC]Milk Visits S
Is there any H on the path?
is equivalent to
are all letters on the path G?
use union find set to merge adjcent same letters.

http://www.usaco.org/index.php?page=viewproblem2&cpid=992
P6004 [USACO20JAN]Wormhole Sort S
sort all edges, add them one by one.

3 2 1 4
3 1 2 4
2 1 3 4
1 2 3 4

include <bits/stdc++.h>

using namespace std;
int n, m;
int p[100020];
int x[100020];
int y[100020];
int z[100020];
int f[100020];
int F(int x)
{
return f[x] != x ? f[x] = F(f[x]) : x;
}
void U(int x, int y)
{
x = F(x);
y = F(y);
f[x] = y;
}
bool ok(int M)
{
for (int i = 1; i <= n; i++)
{
f[i] = i;
}
for (int i = 0; i < m; i++)
{
if (z[i] >= M)
{
U(x[i], y[i]);
}
}
for (int i = 1; i <= n; i++)
{
if (F(i) != F(p[i]))
{
return false;
}
}
return true;
}
int main()
{
freopen("wormsort.in", "r", stdin);
freopen("wormsort.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &p[i]);
}
for (int i = 0; i < m; i++)
{
scanf("%d%d%d", &x[i], &y[i], &z[i]);
}
int L = 0;
int R = 1000000002;
while (L < R - 1)
{
int M = (L + R) / 2;
if (ok(M))
{
L = M;
}
else
{
R = M;
}
}
// L largest, we can sort
// R smallest we can not sort
// L == R-1
// if L is infinity, then -1
if (L == 1000000001)
{
L = -1;
}
printf("%d\n", L);
return 0;
}

(two pointer)

some problems time complexity is less than input size, which is hard for me.
find the median of two sorted arrays. in log(n+m) time
https://leetcode.com/problems/median-of-two-sorted-arrays/
In fact, many differences in time complexity theory can not be distinguished by programs.

http://www.usaco.org/index.php?page=viewproblem2&cpid=950
P5423 [USACO19OPEN]Valleys P

sort (height, x, y) by height increasing, add them one by one
each time the new added and up/down/left/right forms a new valley.
but the new valley might has a hole.

ooo
o.o
ooo

1 + 1 + 1 + 3 + 2 + 6 + 7 + 8 + 9 = 38

count the corners, to detect the hole.

abc
def
ghi

change e from . to o
we need consider
ab
de

bc
ef

de
gh

ef
hi

from
00
00
to
10
00
+1

from
01
00
to
11
00
-1

from
00
01
to
10
01
+1

from
01
10
to
11
10
-3

from
01
01
to
11
01
-1

from
01
11
to
11
11
+1

if the number of corners is 4, then this component should be added to answer

http://www.usaco.org/index.php?page=viewproblem2&cpid=384
P3101 [USACO14JAN]Ski Course Rating G

sort all edges, add them one by one.
maintain the size of each component

I think the number of starting points in the component should be maintained
but the official solution does not.

http://poj.org/problem?id=1988
P2342 [USACO04OPEN]Cube Stacking G

compress the path with information.
the size of each component is also needed.


P3367 【模板】并查集
模板题,一边合并,一边询问

P1551 亲戚
先合并,再询问

  1. BFS/DFS
  2. 并查集

理论上 BFS/DFS 更快
但是大家都会写 并查集
因为 BFS/DFS 需要存边

include <bits/stdc++.h>

using namespace std;
vector a[5020];
int f[5020];
int n, m, q, x, y, cc;
void dfs(int x)
{
if (f[x])
{
continue;
}
f[x] = cc;
for (int y: a[x])
{
dfs(y);
}
}
int main() {
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) {
f[i] = i;
}
for (int i = 0; i < m; i++) {
cin >> x >> y;
a[x].push_back(y);
a[y].push_back(x);
}
for (int i = 1; i <= n; i++)
{
if (f[i] == 0)
{
++cc;
dfs(i);
}
}
for (int i = 0; i < q; i++) {
cin >> x >> y;
if (f[x] == f[y]) {
printf("Yes\n");
} else {
printf("No\n");
}
}
return 0;
}

P1536 村村通

int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (F(i) == i) // 如果i是一个根节点
{
cnt++;
}
}
也可以数连通块的个数

P1955 [NOI2015]程序自动分析
map离散化点的标号

P1333 瑞瑞的木棍
一笔画的条件

  1. 度数为奇数的点 <= 2 个
  2. 所有边连通,可以有孤立的点

int get(string s) {
if (g.find(s) == g.end()) {
g[s] = n;
f[n] = n;
n++;
}
return g[s];
}

g.size()==0
g[2] = 123
map在没有的时候,会插入
g.size()==1

int get(string s) {
if (g.find(s) == g.end()) {
g[s] = g[s].size();
// 那么g[s]是多少?
// 未定义行为,不要这么写
// 有可能是插入之前的size,
// 有可能是插入之后的size
}
return g[s];
}

https://www.luogu.com.cn/problem/P2391
P2391 白雪皑皑
4个点 3个操作
0 0 0 0
第一个 3 3
0 0 1 0
第二个 1 3
2 2 2 0
第三个 3 3
2 2 3 0

0 0 0 0
0 0 3 0
2 2 3 0
2 2 3 0

倒序操作,如果一个点已经被染色了,就直接跳过。
需要快速地找到区间中下一个未被染色的是哪个
如果一个点没有被染色 f[i] = i
如果一个点被染色了 f[i] = i + 1
用类似并查集找根节点,压缩路径的方法,可以快速找到下一个未被染色的点
并查集可能会爆栈
递归的函数,层数特别多,会爆栈
void dfs(int x)
{
printf("%d\n", x);
dfs(x+1);
}
int main()
{
dfs(0);
}
爆栈怎么办?

  1. 有一些OJ会开栈 --Wl,stack=262144
  2. 非递归

https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Path_compression
Path compression
Path halving
Path splitting

int F(int x)
{
while (f[x] != x)
{
f[x] = f[f[x]];
x = f[x];
}
return x;
}
不要写 x = f[x] = f[f[x]]
x = 1
int f[3] = {0, 0, 0}

x = f[x] = 2;
修改的是f[1]还是f[2]?
在C++中修改的是f[1]
在Python中修改的是f[2]
是有区别的,避免这么写

P1525 关押罪犯
问能不能把所有存在仇恨的罪犯都分开?
罪犯看做点,仇恨看做边,是一个二分图
判断是不是二分图 => 判断有没有长度为奇数的环

  1. DFS/BFS
  2. 并查集

并查集可以拆点,或者关系型并查集
点i拆成i和i+n,
如果x和y不在一个集合中
那么
x和y+n合并
x+n和y合并

可以结合二分通过这个题目
如果使用并查集,直接排序边,一条一条边加入,不需要二分。

4 6
1 2 28351
3 4 12884
1 3 6618
2 3 3512
1 4 2534
2 4 1805

P2024 [NOI2001]食物链
x x自己的同类
x+n x吃的同类
x+2*n 吃x的同类
如果合并之后发现了
F(x) == F(x + n) || F(x) == F(x + 2 * n)
就说明当前合并的这一次是假话

关系型并查集,在维护父亲节点的同时维护权值的差

假设每个点有个权值0 1 2
如果x和y是同类,那么d[x] == d[y]
如果x吃y,那么d[x] == (d[y] + 1) % 3
如果y吃x,那么d[x] == (d[y] + 2) % 3

P2256 一中校运会之百米跑
map<string, int>
如果太慢的话,就换成 hash

P1196 [NOI2002]银河英雄传说
每个集合的根节点,就是第一个战舰
对于每个集合需要维护集合大小
对于每个点,需要维护这个点到这个点父亲节点中间有多少个战舰
在压缩路径的同时,维护路径上信息

P3631 [APIO2011]方格染色
B B R B R
R B B B B
R R B R B

0 0 1 0 1
1 0 0 0 0
1 1 0 1 0

0 0 1 0 1
1 1 0 1 0
1 1 0 1 0

翻转(行号和列号同时为偶数的位置)位置,变成2x2区域异或为0
事实上,不需要2x2的区域
任意两行,两列,的四个交点都满足这个性质。

A C E G
B D F H

ABC^D = 0
CDE^F = 0

ABE^F = 0

假设表格是空的,问方案数?
第一行 第一列 随便填 其他位置可以推出来
2 ** (n + m - 1)

a[i][j] = x[i]^y[j]
问{x[1 .. n], y[1 .. m]}解数是多少?

假设所有x[i]和y[j]同时取反,a数组并不会变
所以加一个限制y[1] = 0;

P1455 搭配购买
P2170 选学霸
并查集和背包结合在一起

P6111 [USACO18JAN]MooTube S
P4185 [USACO18JAN]MooTube G
大概思路:
把所有边和所有询问按照关联性从大到小一起排序
如果关联性相同,先排边,再排询问
实现起来,一起排序比较简单

每个结构体包含
类别:询问还是边?
询问:k是多少?v是多少?这是第几个询问
边:起点x,终点y,边权k。

询问 make_pair(k, make_pair(-i -第几个询问, v))
边 make_pair(k, make_pair(x, y))
直接排序的话,边一定排在点的前面

P5836 [USACO19DEC]Milk Visits S

P6004 [USACO20JAN]Wormhole Sort S
满足二分
只用 >= x + 1 的虫洞可以,
那么只用 >= x 的虫洞一定也可以

如何判定?

https://www.luogu.com.cn/problem/P4269
P4269 [USACO18FEB]Snow Boots G

Union Find Set
Doubly Linked List

P1111 修复公路
P3958 奶酪
P1197 星球大战

最小生成树
Kruskal
所有边权之和最小
最大边最小
次大边最小
第三大边最小...

P3366 【模板】最小生成树
P1111 修复公路
P2820 局域网
P1547 [USACO05MAR]Out of Hay S
P2126 Mzc家中的男家丁
P1195 口袋的天空
P2121 拆地毯
P1194 买礼物
P1396 营救

并查集

https://atcoder.jp/contests/abc177/tasks/abc177_d
Friends

https://atcoder.jp/contests/abc120/tasks/abc120_d
Decayed Bridges

https://atcoder.jp/contests/arc065/tasks/arc065_b
Connectivity

https://atcoder.jp/contests/arc107/tasks/arc107_c
Shuffle Permutation

https://codeforces.com/problemset/problem/445/B
每个化学物质看做一条边,每个连通块大小-1之和是答案

https://codeforces.com/problemset/problem/1209/D
每个guest看做一条边,每个连通块可以满足大小-1个人

https://codeforces.com/problemset/problem/1213/G
离线kruskal,问多少对点之间连通

https://codeforces.com/problemset/problem/1411/C
车 移动主对角线

spoj QN02

priority_queue

priority_queue 优先队列/堆

加入任意值
询问最大值
删除最大值

优先队列是唯一一个默认大的在前的内置函数
priority_queue is the only exception, which largest is the first

其他的比如
set
map
sort
lower_bound
都是小的在前
the first is the smallest

每个位置都 -= M 可以删去一段,和 < 0 说明 ans < M R = M
每个位置都 -= M 删去任意一段,和 >= 0 说明 ans >= M L = M

include

std::priority_queue

3个操作
加入一个数字 push
取出最大值 top
删去最大值 pop
(不能删去任意值)
如何支持删除

  1. 直接用set
  2. 每次取top之后,检查一下是不是最新版本
  3. 用一个堆,记录删去了哪些元素,如果2个堆的堆顶相同,同时删去。

可以删除 相当于可以修改

priority_queue q;

priority_queue
可以是结构体
结构体需要重载 <
或者传比较函数

priority_queue 默认 大的数字在前 top 是最大的
这个和 其他STL都不相同

map set sort lower_bound ...
都是小的在前

priority_queue 支持的操作和 set/multiset 很相似
priority_queue 快一点,但是不支持删除任意元素
set/multiset 可以删除任意元素,set支持lower_bound,前驱,后继

lower_bound 查询set中 >=x 最小元素是什么?
upper_bound 查询set中 >x 最小元素是什么?

std::priority_queue<int, std::vector<int>, std::less<int> > q2;
等价于
std::priority_queue<int> q2;
大 的在前

std::priority_queue<int, std::vector<int>, std::greater<int> > q2;
小 的在前

堆是如何实现的
https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3378.cpp
如何用数组存储二叉树
根节点是0,x的左孩子是2x+1,右孩子是2x+2,父亲节点是(x-1)/2
根节点是1,x的左孩子是2x,右孩子是2x+1,父亲节点是x/2

堆的性质(以大根堆为例)
每个节点>=自己的两个孩子
最大值一定在根节点(堆顶)
https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P3378.cpp

调整操作
up 向上调整
每次和自己的父亲节点比

down 向下调整
和自己较大的孩子比
(自己可能0个孩子,1个孩子,2个孩子)

  1. 取最大值
    直接返回堆顶

  2. 如何加入一个数字
    加在末尾,向上调整

  3. 删除
    堆顶和最后一个交换,堆顶向下调整

  4. 初始化
    输入一个数组,如何初始化成堆的样子。

显而易见的做法,一个一个加入
for (int i = 1; i <= n; i++)
{
up(i);
}
时间复杂度O(n log n)

另一个做法
for (int i = n; i >= 1; i--)
{
down(i);
}
O(n)

对于自己实现的堆,可以支持删除任意值
需要记录每个值的位置(所以在所有swap的时候,需要同时交换位置)

swap(a[需要删除的位置], a[n]);
n--;
up(需要删除的位置)
down(需要删除的位置)

P1090 合并果子
Huffman Code

k叉?(每次合并<=k堆)
合并一次减少k-1堆
如果(n-1)%(k-1)==0 和之前一样,贪心即可。
如果(n-1)%(k-1)>0 第一次合并最小的(n-1)%(k-1)+1堆,其他和之前一样。

P1334 瑞瑞的木板
和 合并果子 一样
需要long long

STL函数
make_heap
pop_heap
push_heap

P6033 合并果子 加强版
合并果子的 计数排序 队列优化

P1177 【模板】堆排序

快速排序
时间 O(n log n)
(额外)空间 O(log n)
求第k大数 O(n)
nth_element

堆排序 O(n log n)
(额外)空间 O(1)
最坏时间复杂度O(n log n)

O(n)建堆
pop n次

归并排序 O(n log n)
(额外)空间 O(n)
求逆序对 O(n log n)
可以用硬盘,不一定都要在内存
可以从2个文件中读入,输出到第3个文件
比如你需要排序非常非常大的文件,10G

实际题目需要排序的话,一定是
sort 函数
introsort
效率非常高
主要掌握比较函数如何实现

P1168 中位数

#include <bits/stdc++.h>
using namespace std;
int n, x;
priority_queue<int, vector<int>, greater<int> > q1; 小根堆
priority_queue<int, vector<int>, less<int> > q2; 大根堆
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> x;
		if (i == 1 || x > q1.top()) {
			q1.push(x);
		} else {
			q2.push(x);
		}
		if (q1.size() < q2.size()) {
			q1.push(q2.top());
			q2.pop();
		}
		if (q1.size() > q2.size() + 1) {
			q2.push(q1.top());
			q1.pop();
		}
		// 时刻保证 q1.size() == q2.size() || q1.size() == q2.size() + 1
		// 时刻保证 q1.top() >= q2.top();
		if (i % 2 == 1) {
			printf("%d\n", q1.top());
		}
	}
	return 0;
}

P1801 黑匣子
和中位数类似的思路,2个堆

P1631 序列合并

注意到a[i] + b[j] 如果ij>n 一定不会被选择
考虑有多少个位置满足 i
j <= n
n / 1 + n / 2 + .. + n / n 约等于 n log n
在n log n个数字中求最小的n个

for (int i = 1; i <= n && i <= a.size(); i++)
{
	for (int j = 1; j * i <= n && j <= b.size(); j++)
	{
		re.push_back(a[i-1] + b[j-1]);
	}
}
sort(re.begin(), re.end());
re.resize(n);
return re;

P2085 最小函数值
Fi(x) < Fi(x+1)
一定先取x再取x+1

P3620 [APIO/CTSC 2007]数据备份
2 1 2 6

1000000000 1 2 3 4

a b c 选择中间的b
变成1个数字
a+c-b

1380 夹克老爷的逢三抽一

P1878 舞蹈课

P5120 [USACO18DEC]Convention II S 用堆贪心

P1382 楼房
扫描线 set

P2707 Facer帮父亲
P2085 最小函数值

P3045 [USACO12FEB]Cow Coupons G

优先队列 可以优化 dijkstra

Java PriorityQueue

P2107
带反悔的贪心
一定是从原点向右走到某个位置,在路过的任务中选最小的若干个来做。

P2949
带反悔的贪心

假设决定了做哪些任务,一定是按照截止时间排序来做。
对于任意一个时间x,
截止时间<=x的任务 消耗的总时间 必须<=x
截止时间<=x的任务 个数 必须<=x

扫描所有任务,每次直接选择,如果当前选择的任务个数 > 截止时间,在所有已经选择的任务中,
扔掉获利最小的(扔掉获利最小的任务时,不考虑截止时间)

假设每个任务消耗的时间不相同
可以用背包解决
先排序
每个任务更新时,只更新到自己的截止时间

背包时,不同物品的放置顺序是有关系的
先考虑假设要选择一个集合
按哪种顺序一定可以放置成功
先把这些物品按照这个顺序排序

https://codeforces.com/gym/101623/attachments
https://codeforces.com/gym/101623/problem/I

树状数组

名称

Fenwick Tree / Binary Indexed Tree

Lowbit

lowbit(i)定义为能整除ii的最大的22的次幂。也可以理解为将ii转为二进制之后最后一个11的权重。

lowbit(i)可以通过位运算i & -i很快的计算。

lowbit(x) x的约数中,最大的2的次幂
lowbit(12) = 4
1100
lowbit(13) = 1
1101
lowbit(x) = x&-x

基本原理

设原数组为{ai}(1in)\{a_i\} (1 \leq i \leq n)定义新数组cic_i,其中
ci=ilowbit(i)<jiajc_i = \sum_{i - lowbit(i) < j \leq i} a_j
这样定义的优势,对于任意一个前缀,均可以用O(logn)O(\log n)项拼出。
修改其中任意一项,都只需要修改O(logn)O(\log n)个位置。

基本操作

修改操作

void change(int x, int y) {
    for (int i = x; i <= n; i += i & -i) {
        c[i] += y;
    }
}

查询操作

int query(int x) {
    int re = 0;
    for (int i = x; i > 0; i -= i & -i) {
        re += c[i];
    }
    return re;
}

kk大操作

int ask(int x) {
    int re = 0;
    for (int i = 1 << 19; i > 0; i >>= 1) {
        if (re + i <= n && x > c[re + i]) {
            x -= c[re += i];
        }
    }
    return re + 1;
}

O(n)O(n)建树状数组

和堆一样,如果希望O(n)O(n)构造一个树状数组,可以直接for循环。

树状数组,STL set与平衡树

许多平衡树的题目并不真的需要平衡树。

  1. 如果可以离线,询问第kk大,那么可以离线,离散化,树状数组。
  2. 如果强制在线,不问第kk大,只问前驱后继,那么可以STL set

其他推广

树状数组对于维护的信息要求比较苛刻,要求既可以加法,又可以加法。
所以常见的维护内容就是加法和异或。

在满足一定条件下(越改越大,只询问前缀)最大值的查询也可以用树状数组完成,这在一些DP题中有应用。

高维推广 / 二维树状数组

树状数组可以很容易的推广到高维的情况,单次操作时间复杂度为O(log2n)O(\log^2 n)从实际经验来看,基本只有二维和三维有利用价值。

c[i][j] = a[i - lowbit(i) + 1 .. i][j - lowbit(j) + 1 .. j] 共lowbit(i) * lowbit(j) 个数字
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j]
a[i][j] = s[i][j] - s[i-1][j] - s[i][j-1] + s[i-1][j-1]
b[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]

参考题目

poj 2352
二维偏序,排序一维,树状数组做一维。

poj 3468
可以使用线段树,也可以使用

poj 2155
二维树状数组,异或。

133. 二维树状数组 1:单点修改,区间查询

https://loj.ac/problem/133

c[i][j] = a[i - lowbit(i) + 1 .. i][j - lowbit(j) + 1 .. j]

134. 二维树状数组 2:区间修改,单点查询

https://loj.ac/problem/134

135. 二维树状数组 3:区间修改,区间查询

https://loj.ac/problem/135

希望求 s[n][m]

s[n][m] = sum(a[i][j], 1 <= i <= n, 1 <= j <= m)
a[i][j] = sum(b[k][l], 1 <= k <= i, 1 <= l <= j)

s[n][m] = sum(sum(b[k][l], 1 <= k <= i, 1 <= l <= j), 1 <= i <= n, 1 <= j <= m)
s[n][m] = sum(sum(b[k][l] * (n+1-k) * (m+1-l), 1 <= k <= n, 1 <= l <= m))

用二维树状数组维护 b[k][l]
用二维树状数组维护 b[k][l] * k
用二维树状数组维护 b[k][l] * l
用二维树状数组维护 b[k][l] * k * l

b[k][l] * (n+1-k) * (m+1-l) =
b[k][l] * ((n+1) * (m+1) - k * (m+1) - l * (n+1) + k * l)

https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/
https://cp-algorithms.com/data_structures/fenwick.html

http://poj.org/problem?id=2155
http://poj.org/problem?id=2352
http://poj.org/problem?id=2828
http://poj.org/problem?id=3468
http://www.spoj.com/problems/INVCNT/

https://atcoder.jp/contests/abc185/tasks/abc185_f

Error: ENOENT: no such file or directory, open '/Users/wwwwodddd/Dropbox/Informatics/problems/P4514'
Error: ENOENT: no such file or directory, open '/Users/wwwwodddd/Dropbox/Informatics/problems/P3369'

P3960 [NOIP2017 提高组] 列队

https://www.luogu.com.cn/problem/P3960

题目背景

题目描述

Sylvia 是一个热爱学习的女孩子。

前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。

Sylvia 所在的方阵中有 n×mn \times m 名学生,方阵的行数为 nn,列数为 mm

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 11n×mn \times m 编上了号码(参见后面的样例)。即:初始时,第 ii 行第 jj 列 的学生的编号是 (i1)×m+j(i-1)\times m + j

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 qq 件这样的离队事件。每一次离队事件可以用数对 (x,y)(1xn,1ym)(x,y) (1 \le x \le n, 1 \le y \le m) 描述,表示第 xx 行第 yy 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

  1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 xx 行第 mm 列。

  2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 nn 行第 mm 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 nn 行 第 mm 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学 的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

输入格式

输入共 q+1q+1 行。

第一行包含 33 个用空格分隔的正整数 n,m,qn, m, q,表示方阵大小是 nnmm 列,一共发 生了 qq 次事件。

接下来 qq 行按照事件发生顺序描述了 qq 件事件。每一行是两个整数 x,yx, y,用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 xx 行第 yy 列。

输出格式

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学生的编号。

样例 #1

样例输入 #1
2 2 3 
1 1 
2 2 
1 2 
样例输出 #1
1
1
4

提示

【输入输出样例 11 说明】

列队的过程如上图所示,每一行描述了一个事件。 在第一个事件中,编号为 11 的同学离队,这时空位在第一行第一列。接着所有同学 向左标齐,这时编号为 22 的同学向左移动一步,空位移动到第一行第二列。然后所有同 学向上标齐,这时编号为 44 的同学向上一步,这时空位移动到第二行第二列。最后编号为 11 的同学返回填补到空位中。

【数据规模与约定】

测试点编号 nn mm qq 其他约定
161\sim 6 103\le 10^3 103\le 10^3 500\le 500
7107\sim 10 5×104\le 5\times 10^4 5×104\le 5\times 10^4 500\le 500
111211\sim 12 =1=1 105\le 10^5 105\le 10^5 所有事件 x=1x=1
131413\sim 14 =1=1 3×105\le 3\times 10^5 3×105\le 3\times 10^5 所有事件 x=1x=1
151615\sim 16 3×105\le 3\times 10^5 3×105\le 3\times 10^5 3×105\le 3\times 10^5 所有事件 x=1x=1
171817\sim 18 105\le 10^5 105\le 10^5 105\le 10^5
192019\sim 20 3×105\le 3\times 10^5 3×105\le 3\times 10^5 3×105\le 3\times 10^5

数据保证每一个事件满足 1xn,1ym1 \le x \le n,1 \le y \le m

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, q, x[300020], y[300020], N;
long long z[300020];
int c[600020];
vector<pair<int, int> > a[300020];
vector<long long> b[300020], l;
void R(int x, int y) {
	for (; x <= N; x += x & -x) {
		c[x] += y;
	}
}
int A(int x) {
	int re = 0;
	for (int i = 1 << 20; i > 0; i /= 2) {
		if (re + i <= N && c[re + i] < x) {
			x -= c[re += i];
		}
	}
	return re + 1;
}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	N = max(n, m) + q;
	for (int i = 0; i < q; i++) {
		scanf("%d%d", &x[i], &y[i]);
		if (y[i] < m) {
			a[x[i]].push_back(make_pair(y[i], i));
		}
	}
	for (int i = 1; i <= N; i++) {
		c[i]++;
		if (i + (i & -i) <= N) {
			c[i + (i & - i)] += c[i];
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < a[i].size(); j++) {
			int u = A(a[i][j].first);
			z[a[i][j].second] = u;
			R(u, -1);
		}
		for (int j = 0; j < a[i].size(); j++) {
			R(z[a[i][j].second], 1);
		}
	}
	for (int i = 0; i <= n; i++) {
		l.push_back((long long)i * m);
	}
	for (int i = 0; i < q; i++) {
		int u = A(x[i]);
		R(u, -1);
		if (y[i] < m) {
			b[x[i]].push_back(l[u]);
			if (z[i] < m) {
				z[i] = (long long)(x[i] - 1) * m + z[i];
			} else {
				z[i] = b[x[i]][z[i] - m];
			}
		} else {
			z[i] = l[u];
		}
		l.push_back(z[i]);
	}
	for (int i = 0; i < q; i++) {
		printf("%lld\n", z[i]);
	}
	return 0;
}

题解

这个题目需要询问第kk11的位置,所以可以树状数组。

P3374 【模板】树状数组 1

https://www.luogu.com.cn/problem/P3374

题目背景

题目描述

如题,已知一个数列,你需要进行下面两种操作:

输入格式

第一行包含两个正整数 n,mn,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 33 个整数,表示一个操作,具体如下:

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

样例 #1

样例输入 #1
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
样例输出 #1
14
16

提示

【数据范围】

对于 30%30\% 的数据,1n81 \le n \le 81m101\le m \le 10
对于 70%70\% 的数据,1n,m1041\le n,m \le 10^4
对于 100%100\% 的数据,1n,m5×1051\le n,m \le 5\times 10^5

样例说明:

故输出结果14、16

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int c[500020];
void change(int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] += y;
	}
}
int query(int x) {
	int re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re += c[i];
	}
	return re;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		int x;
		scanf("%d", &x);
		change(i, x);
	}
	for (int i = 0; i < m; i++) {
		int o, x, y;
		scanf("%d%d%d", &o, &x, &y);
		if (o == 1) {
			change(x, y);
		} else {
			printf("%d\n", query(y) - query(x - 1));
		}
	}
	return 0;
}

题解

a[l] + a[l+1] + a[l+2] + ... + a[r-1] + a[r] == query(r) - query(l-1)

时间复杂度

暴力:
修改 O(1)
change O(1)
询问 O(n)
query O(n)

暴力维护前缀和:
修改 O(n)
change O(n)
询问 O(1)
query O(1)

树状数组
修改 O(log n)
询问 O(log n)

原数组 a[]
树状数组 c[]

c[i] = a[i - lowbit(i) + 1] + a[i - lowbit(i) + 2] + ... + a[i - 1] + a[i] (共lowbit(i)项)

c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4]
c[5] = a[5]
c[6] = a[5] + a[6]
c[7] = a[7]
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]

修改a[i],只会影响到 log n 个 c[i]
修改a[5]只需要修改c[5] c[6] c[8]...

void change(int x, int y)
{
    for (; x <= n; x += x & -x)
    {
        c[x] += y;
    }
}

询问前i个数字的和,只需要用 log n 个 c[i] 拼出来
前5个的和 = c[5] + c[4]

int query(int x)
{
    int re = 0;
    for (; x > 0; x -= x & -x)
    {
        re += c[x];
    }
    return re;
}

A Simple Problem

maintain an array a[]

supports
length of array 100000
the number of operations 100000

  1. Given x, v; a[x] += v;
  2. GetSum l, r: a[l] + a[l+1] + ... + a[r]

Two prefix query
a[1] + a[2] + .. + a[l-1]
a[1] + a[2] + .. + a[r]

暴力 bruteforce

修改 update O(1)
询问 query O(n)

分sqrt(n)

修改 change O(2) = O(1)
询问 query O(2 * sqrt(n))
分3层
第一层The first layer each block contain 1 每块1个
第二层The second layer each block contain n**(1/3) 每块n**(1/3)个
第三层The third layer each block contain n**(2/3) 每块n**(2/3)个
修改 update O(3)
询问 query O(3 * n**(1/3))

分log n层
log n layers
第一层每块1个 The first layer contains 1
第二层每块2个 The second layer contains 2
第三层每块4个 The thrid layer contains 4
修改O(log n)
询问O(log n)

前缀和 prefix sum
部分和 partial sum
s[i] = a[1] + a[2] + .. a[i]
a[l] + a[l+1] + ... + a[r] = s[r] - s[l-1]

暴力维护前缀和

每当修改a[i]时,都重新求一次s

修改 change O(n)
询问 query O(1)

树状数组 binary indexed tree

The binary indexed tree achieves a much better balance between
two operations: element update and prefix sum calculation.

修改 change O(log n)
询问 query O(log n)

the original array: a[1], a[2] ... a[n]
the bit array : c

c[i] = a[i - lowbit(i) + 1] + ... + a[i]

c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4]
c[5] = a[5]
c[6] = a[5] + a[6]
c[7] = a[7]
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]

When change a[i], we need to change at most log n c[i]
When sum a[1] + a[2] + ... + a[i], at most log n c[i]
修改
void change(int x, int y)
{
// a[x] += y;
// 不是 not a[x] = y
// increment not assignment
for (; x <= n; x += lowbit(x))
{
c[x] += y;
}
}

询问
int query(int x) // return a[1] + a[2] + ... + a[x]
{
int re = 0;
for (; x > 0; x -= lowbit(x))
{
re += c[x]
}
return re;
}

P3368 【模板】树状数组 2

https://www.luogu.com.cn/problem/P3368

题目背景

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 xx

  2. 求出某一个数的值。

输入格式

第一行包含两个整数 NNMM,分别表示该数列数字的个数和操作的总个数。

第二行包含 NN 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 MM 行每行包含 2244个整数,表示一个操作,具体如下:

操作 11: 格式:1 x y k 含义:将区间 [x,y][x,y] 内每个数加上 kk

操作 22: 格式:2 x 含义:输出第 xx 个数的值。

输出格式

输出包含若干行整数,即为所有操作 22 的结果。

样例 #1

样例输入 #1
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4
样例输出 #1
6
10

提示

样例 1 解释:

故输出结果为 6、10。


数据规模与约定

对于 30%30\% 的数据:N8N\le8M10M\le10

对于 70%70\% 的数据:N10000N\le 10000M10000M\le10000

对于 100%100\% 的数据:1N,M5000001 \leq N, M\le 5000001x,yn1 \leq x, y \leq n,保证任意时刻序列中任意元素的绝对值都不大于 2302^{30}

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int c[500020];
int a[500020];
void change(int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] += y;
	}
}
int query(int x) {
	int re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re += c[i];
	}
	return re;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	for (int i = 0; i < m; i++) {
		int o, x, y, k;
		scanf("%d", &o);
		if (o == 1) {
			scanf("%d%d%d", &x, &y, &k);
			change(x, k);
			change(y + 1, -k);
		} else {
			scanf("%d", &x);
			printf("%d\n", query(x) + a[x]);
		}
	}
	return 0;
}

题解

0 1 5 4 2 3
0 1 4 -1 -2 1
操作 1 2 4 2
0 1 6 -1 -2 -1
操作 2 3,回答 1 6 -1

差分

a[1] a[2] a[3] ... a[n]

b[i] = a[i] - a[i-1]
a[i] = b[1] + b[2] + ... + b[i]

a[l-1] a[l] a[l+1] ... a[r-1] a[r] a[r+1]
b[l] = a[l] - a[l-1]
b[r+1] = a[r+1] - a[r]

a[l-1] a[l]+x a[l+1]+x ... a[r-1]+x a[r]+x a[r+1]
b[l] = a[l]+x - a[l-1]
b[r+1] = a[r+1] - a[r]-x

相当于修改b中的2个点,询问b的前缀和

初始全是 0
0 0 0 0 0 0

第3到第5个数字+=2
0 0 2 2 2 0

对于
0 0 2 2 2 0
求差分可以得到
0 0 2 0 0 -2

在差分上只改了2个位置
第3个位置+=2
第6个位置-=2

对于
0 0 2 0 0 -2
求前缀和可以得到
0 0 2 2 2 0

第2到第4个数字+=3
0 3 5 5 2 0
差分是
0 3 2 0 -3 -2

在差分上只改了2个位置
第2个位置+=3
第5个位置-=3

差分
b[i] = a[i] - a[i-1]

a[i] = b[1] + b[2] + b[3] + ... + b[i]

a数组中修改一个区间,b数组中只改变了2个位置
a: 0 0 0 x x x x 0 0 0 0
b: 0 0 0 +x 0 0 0 -x 0 0 0
index l i r r+1

P2068 统计和

https://www.luogu.com.cn/problem/P2068

题目背景

题目描述

给定一个长度为 n(n100000)n(n\leq 100000),初始值都为 00 的序列,x(x100000)x(x\leq 100000) 次的修改某些位置上的数字,每次加上一个数,然后提出 y(y100000)y(y\leq 100000) 个问题,求每段区间的和。

输入格式

第一行 11 个整数,表示序列的长度 nn

第二行 11 个整数,表示操作的次数 ww

后面依次是 ww 行,分别表示加入和询问操作。

其中,加入用 x 表示,询问用 y 表示。

xx的格式为 x a b 表示在序列上第 aa 个数加上 bb。保证 1an1 \leq a \leq n1b1091 \leq b \leq 10^9

yy 的格式为 y a b 表示询问 aabb 区间的加和。保证 1abn1 \leq a \leq b \leq n

输出格式

每行一个正整数,分别是每次询问的结果

样例 #1

样例输入 #1
5
4
x 3 8
y 1 3
x 4 9
y 3 4
样例输出 #1
8
17

提示

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long c[100020];
void change(int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] += y;
	}
}
long long query(int x) {
	long long re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re += c[i];
	}
	return re;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++) {
		char o;
		int x, y;
		scanf(" %c%d%d", &o, &x, &y);
		if (o == 'x') {
			change(x, y);
		} else {
			printf("%lld\n", query(y) - query(x - 1));
		}
	}
	return 0;
}

题解

读入字符/字符串
char o;
int x, y;
scanf(" %c%d%d", &o, &x, &y);
//" "跳过空白字符,"%c"读入非空字符

char o[2];
scanf("%s%d%d", o, &x, &y);
//读入一个以空白分开的一段的字符串

a[i]

b[i] = a[i] - a[i-1]

a[x] += k, a[x+1] += k, ..., a[y]+=k
b[x] +=k, b[y+1] -= k
a[x] = b[1] + b[2] + ... + b[x]

对比 线段树 树状数组
树状数组快一点,好写一点

树状数组支持高维

线段树只需要支持区间合并

另一种树状数组表示方法
a[1] = c[1] + c[2] + c[4] + c[8]
a[2] = c[2] + c[4] + c[8]
a[3] = c[3] + c[4] + c[8]
a[4] = c[4] + c[8]
a[5] = c[5] + c[6] + c[8]
a[6] = c[6] + c[8]
a[7] = c[7] + c[8]
a[8] = c[8]

询问a数组中一个位置的值
int query(int x)
{
int re = 0;
for (; x <= n; x += x & -x)
{
re += c[x];
}
return re;
}

修改a数组的一个前缀
void change(int x, int y)
{
for (; x > 0; x -= x & -x)
{
c[x] -= y;
}
}

P5057 [CQOI2006]简单题

https://www.luogu.com.cn/problem/P5057

题目背景

题目描述

有一个 n 个元素的数组,每个元素初始均为 0。有 m 条指令,要么让其中一段连续序列数字反转——0 变 1,1
变 0(操作 1),要么询问某个元素的值(操作 2)。
例如当 n = 20 时,10 条指令如下:

输入格式

第一行包含两个整数 n, m,表示数组的长度和指令的条数; 以下 m 行,每行的第一个数 t 表示操作的种类:

若 t = 1,则接下来有两个数 L, R,表示区间 [L, R] 的每个数均反转; 若 t = 2,则接下来只有一个数 i,表示询问的下标。

输出格式

每个操作 2 输出一行(非 0 即 1),表示每次操作 2 的回答。

样例 #1

样例输入 #1
20 10
1 1 10
2 6
2 12
1 5 12
2 6
2 15
1 6 16
1 11 17
2 12
2 6
样例输出 #1
1
0
0
0
1
1

提示

对于 50% 的数据,1 ≤ n ≤ 10310^3, 1 ≤ m ≤ 10410^4
对于 100% 的数据,1 ≤ n ≤ 10510^5, 1 ≤ m ≤ 5 × 10510^5,保证 L ≤ R。

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int c[100020];
void change(int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] ^= y;
	}
}
int query(int x) {
	int re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re ^= c[i];
	}
	return re;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++) {
		int o, x, y;
		scanf("%d", &o);
		if (o == 1) {
			scanf("%d%d", &x, &y);
			change(x, 1);
			change(y + 1, 1);
		} else {
			scanf("%d", &x);
			printf("%d\n", query(x));
		}
	}
	return 0;
}

题解

只要可逆操作,就可以用树状数组做

可逆操作

  1. 加法 减法 (a+b) = c a = (c-b)
  2. 异或 异或 (a^b) = c a = (c^b)
  3. 乘法 乘法逆元

不是可逆操作

  1. max max(a, b) = c 无法根据c和b求出a

一类特殊的树状数组,如果

  1. 修改只会越改越大
  2. 询问只有前缀
    那么树状数组也可以维护最大值

异或的差分
b[i] = a[i] ^ a[i-1]
a[x] ^= k, a[x+1] ^= k, ..., a[y]^=k
b[x] ^=k, b[y+1] ^= k
a[x] = b[1] ^ b[2] ^ ... ^ b[x]

P5142 区间方差

https://www.luogu.com.cn/problem/P5142

题目背景

出题人并没有能力写有趣的题面……

题目描述

对于一个长度为 nn 的序列 a1,a2,a3ana_1,a_2,a_3\cdots a_n,我们定义它的平均数 aa 为:

a=1ni=1naia=\frac{1}{n}\sum_{i=1}^{n}a_i

并定义它的方差 dd 为:

d=1ni=1n(aia)2d=\frac{1}{n}\sum_{i=1}^{n}(a_i-a)^2

现在给定一个长度为 nn 的序列 b1,b2bnb_1,b_2\cdots b_n。你需要支持两种操作。每种操作的格式为 c x y

c=1c=1,为修改操作,代表将 bxb_x 赋值为 yy

c=2c=2,为查询操作,代表查询 bxb_xbyb_y 的方差。

为了避免浮点数误差,请以分数取模形式输出结果(对 1000000007(109+710^9+7)取模)。

输入格式

第一行两个整数 n,mn,m,代表序列 bb 的长度为 nn,有 mm 个操作。

第二行 nn 个整数 bib_i,表示序列 bb 的初始值。

下面有 mm 行整数,每行格式为 c x y,含义如上文所示。保证所有操作合法。

输出格式

对于每个操作 2,输出一行。

样例 #1

样例输入 #1
4 8
0 0 0 0
1 1 1
1 2 2
1 3 3
1 4 4
2 1 1
2 1 2
2 1 3
2 1 4
样例输出 #1
0
250000002
666666672
250000003

提示

样例 1 解释

四次修改后,序列 bb 为:{1,2,3,4}\{1,2,3,4\}

区间 [1,1][1,1] 的方差为 00

区间 [1,2][1,2] 的方差为 14\frac{1}{4}44 的逆元为 250000002250000002

区间 [1,3][1,3] 的方差为 23\frac{2}{3}33 的逆元为 3333333363333333362×333333336modM=6666666722\times333333336\bmod M=666666672

数据规模与约定

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m, p = 1000000007;
int a[100020];
int c1[100020];
int c2[100020];
void change(int *c, int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] += y;
		if (c[i] >= p) {
			c[i] -= p;
		}
	}
}
int query(int *c, int x) {
	int re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re += c[i];
		if (re >= p) {
			re -= p;
		}
	}
	return re;
}
int inv(int x) {
	if (x == 1) {
		return 1;
	}
	return (long long)(p - p / x) * inv(p % x) % p;
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		change(c1, i, a[i]);
		int t2 = (long long)a[i] * a[i] % p;
		change(c2, i, t2);
	}
	for (int i = 0; i < m; i++) {
		int o, x, y;
		scanf("%d%d%d", &o, &x, &y);
		if (o == 1) {
			int t1 = -a[x];
			int t2 = -(long long)a[x] * a[x] % p;
			a[x] = y;
			t1 = (t1 + a[x]) % p;
			t2 = (t2 + (long long)a[x] * a[x]) % p;
			if (t1 < 0) {
				t1 += p;
			}
			if (t2 < 0) {
				t2 += p;
			}
			change(c1, x, t1);
			change(c2, x, t2);
		} else {
			int invlen = inv(y - x + 1);
			int t1 = query(c1, y) - query(c1, x - 1);
			int t2 = query(c2, y) - query(c2, x - 1);
			t1 = (long long)t1 * invlen % p;
			t2 = (long long)t2 * invlen % p;
			int ans = (t2 - (long long)t1 * t1) % p;
			if (ans < 0) {
				ans += p;
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}

题解

方差 = 平方的平均数 - 平均数的平方

(1, 3) 的方差是1
平均数是2
平方的平均数是5
方差 = 平方的平均数 - 平均数 * 平均数

注意这个题目的操作不是
a[x] += y
而是
a[x] = y

P3608 [USACO17JAN]Balanced Photo G

https://www.luogu.com.cn/problem/P3608

题目背景

题目描述

Farmer John is arranging his NN cows in a line to take a photo (1N100,0001 \leq N \leq 100,000). The height of the iith cow in sequence is hih_i, and the heights of all cows are distinct.

As with all photographs of his cows, FJ wants this one to come out looking as nice as possible. He decides that cow ii looks "unbalanced" if LiL_i and RiR_i differ by more than factor of 2, where LiL_i and RiR_i are the number of cows taller than ii on her left and right, respectively. That is, ii is unbalanced if the larger of LiL_i and RiR_i is strictly more than twice the smaller of these two numbers. FJ is hoping that not too many of his cows are unbalanced.

Please help FJ compute the total number of unbalanced cows.

FJ正在安排他的N头奶牛站成一排来拍照。(1<=N<=100,000)序列中的第i头奶牛的高度是h[i],且序列中所有的奶牛的身高都不同。

就像他的所有牛的照片一样,FJ希望这张照片看上去尽可能好。他认为,如果L[i]和R[i]的数目相差1倍以上,第i头奶牛就是不平衡的(L[i]和R[i]分别代表第i头奶牛左右两边比她高的奶牛的数量)。也就是说,如果L[i]和R[i]中的较大数大于较小数的两倍,第i头奶牛就是不平衡的。FJ不希望他有太多的奶牛不平衡。

请帮助FJ计算不平衡的奶牛数量。

输入格式

The first line of input contains NN. The next NN lines contain h1hNh_1 \ldots h_N, each a nonnegative integer at most 1,000,000,000.

第一行一个整数N。接下N行包括H[1]到H[n],每行一个非负整数(不大于1,000,000,000)。

输出格式

Please output a count of the number of cows that are unbalanced.

请输出不平衡的奶牛数量。

样例 #1

样例输入 #1
7
34
6
23
0
5
99
2
样例输出 #1
3

提示

感谢 @XY星系质量PK 的翻译

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, z;
int h[100020];
int o[100020];
int c[100020];
void change(int x, int y)
{
	for (; x <= n; x += x & -x)
	{
		c[x] += y;
	}
}
int query(int x)
{
	int re = 0;
	for (; x > 0; x -= x & -x)
	{
		re += c[x];
	}
	return re;
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &h[i]);
		o[i] = i;
	}
	sort(o + 1, o + 1 + n, [](int x,int y){return h[x]>h[y];});
	for (int i = 1; i <= n; i++)
	{
		int l = query(o[i]);
		int r = i - 1 - l;
		if (max(l, r) > 2 * min(l, r))
		{
			z++;
		}
		change(o[i], 1);
	}
	printf("%d\n", z);
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=693

将所有奶牛

sort(o+1,o+1+n,[](int x,int y){return h[x]>h[y];});
o 排名数组
把o按照h降序排序(因为最后是h[x]>h[y]
排序之后
h[o[1]] 是 h数组中最大的数字
h[o[2]] 是 h数组中次大的数字
...
h[o[n]] 是 h数组中最小的数字

Count the number of inversions in a permutation
1 <= a[i] <= n

for (int i = 1; i <= n; i++)
{
// query(n) == i-1
ans += i - 1 - query(a[i]); // a[i+1] + a[i+2] + ... + a[n]
change(a[i], 1);
}

// for each i, count how many j, j < i && a[j] > a[i]
// for each i, count how many j, j > i && a[j] < a[i]

sort
unique
lower_bound

1 20 300 4000
1 2 3 4

离散化
discreting

sort (value, position) in inceasing order

for (int i = 0; i < n; i++)
{
ans += i - query(a[i].position);
// the number of j (a[j].value < a[i].value && a[j].position > a[i].position) after a[i].position;
change(a[i].position, 1);
}

排序
(高度,位置)
从高到低依次处理所有奶牛
a[i].fisrt 高度
a[i].second 位置
for (int i = n-1; i >= 0; i++)
{
// 在自己之前加入的,一定比自己高
int L = query(a[i].second); // 位置在自己的左边
int R = n - 1 - i - L; // 位置在自己的右边
// 不需要询问query(n),因为答案一定是n-1-i
change(a[i].second, 1);
if (max(L, R) > 2 * min(L,R))
{
ans++;
}
}

inversion

i < j && a[i] < a[j] && b[i] < b[j]

非常直接的思路:
高度离散化到1到n
从左向右做一次,可以得到每个数字左边有几个比自己大的
从右向左做一次,可以得到每个数字右边有几个比自己大的

把所有牛按照高度排序(高度, 下标),从高到底
枚举所有的牛,
询问 当前牛左边的和是多少 当前牛右边的和是多少 计算是否平衡
当前牛的位置 += 1

逆序对的个数,等价于冒泡排序,需要的交换次数(只能交换相邻2个)

两类做法:

  1. 归并排序
  2. (离散化/排序)树状数组

非常直接的思路:
直接离散化a数组

按照(a[i], i)排序
扫描所有的(a[i], i)

2 8 0 3
生成
(2, 1)
(8, 2)
(0, 3)
(3, 4)

排序
(0, 3)
(2, 1)
(3, 4)
(8, 2)

答案 += (>i的所有位置之和是多少) = (全部的和 - <=i的位置之和)
位置i += 1

一些外国人的写法
void change(int x, int y)
{
for (x < n; x |= x + 1)
{
c[x] += y;
}
}
int query(int x)
{
int re = ...
}

010001
010011
010111
011111
111111

P6278 [USACO20OPEN]Haircut G

https://www.luogu.com.cn/problem/P6278

题目背景

题目描述

Farmer John 由于对整理他难以整平的头发感到疲惫,于是决定去理发。他有一排 NN 缕头发,第 ii 缕头发初始时长度为 AiA_i 微米(0AiN0\le A_i\le N)。理想情况下,他想要他的头发在长度上单调递增,所以他定义他的头发的“不良度”为逆序对的数量:满足 i<ji < jAi>AjA_i > A_j 的二元对 (i,j)(i,j)
对于每一个 j=0,1,,N1j=0,1,\ldots,N-1,Farmer John 想要知道他所有长度大于 jj 的头发的长度均减少到 jj 时他的头发的不良度。


(有趣的事实:人类平均确实有大约 10510^5 根头发!)

输入格式

输入的第一行包含 NN
第二行包含 A1,A2,,ANA_1,A_2,\ldots,A_N

输出格式

对于每一个 j=0,1,,N1j=0,1,\ldots,N-1,用一行输出 Farmer John 头发的不良度。


注意这个问题涉及到的整数大小可能需要使用 6464 位整数型存储(例如,C/C++ 中的“long long”)。

样例 #1

样例输入 #1
5
5 2 3 3 0
样例输出 #1
0
4
4
5
7

提示

样例解释:

输出的第四行描述了 Farmer John 的头发长度减少到 33 时的逆序对数量。
A=[3,2,3,3,0]A=[3,2,3,3,0] 有五个逆序对:A1>A2,A1>A5,A2>A5,A3>A5,A_1>A_2,\,A_1>A_5,\,A_2>A_5,\,A_3>A_5,A4>A5A_4>A_5


对于 100%100\% 的数据,1N1051\le N\le 10^5

1313 个测试点,其中 11 为样例,其余性质如下:

测试点 22 满足 N100N\le 100
测试点 353\sim 5 满足 N5000N\le 5000
测试点 6136\sim 13 没有额外限制。


出题人:Dhruv Rohatgi

参考代码

#include <bits/stdc++.h>
using namespace std;
int n;
int c[100020];
void change(int x, int y)
{
	for (x++; x <= n + 1; x += x & -x)
	{
		c[x] += y;
	}
}
int query(int x)
{
	int re = 0;
	for (x++; x > 0; x -= x & -x)
	{
		re += c[x];
	}
	return re;
}
long long z[100020];
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int x;
		cin >> x;
		int u = i - query(x);
		z[x] += u;
		change(x, 1);
	}
	long long s = 0;
	for (int i = 0; i < n; i++)
	{
		printf("%lld\n", s);
		s += z[i];
	}
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=1041

5
5 2 3 3 0

0 1 1 1 4

如果j>5那么答案+=0
如果j>2那么答案+=1
如果j>3那么答案+=1
如果j>3那么答案+=1
如果j>0那么答案+=4

For each i
how many j j < i && a[j] > a[i]

contribution the number of inversion, (j, i)

If decrease to something x
if x > a[i] then the contribution of i does not change
cnt[a[i]] += the number of j (j < i && a[j] > a[i])

if x <= a[i] then the contribution of i is 0

Get the answer of x,
sum cnt[1] + cnt[2] + .. + cnt[x-1]

P6278 [USACO20OPEN]Haircut G

5 2 3 3 0
计算每个位置之前有几个比自己大的
0 1 1 1 4
如果和j取min,相当于
所有a[i]>=j的位置贡献变成0
所有a[i]<j的位置贡献不变

ans[n] = 7
ans[n-1] = ans[n] - (a[???] == n-1的位置的贡献)
...
ans[0]

也可以正序解决
ans[0] = 0
ans[1] = ans[0] + (a[???] == 0的贡献)

ans[i] = ans[i-1] + (a[???] == i-1的贡献)

另一种想法
把所有
(a[i], a[i]带来的贡献排序)

P5200 [USACO19JAN]Sleepy Cow Sorting G

https://www.luogu.com.cn/problem/P5200

题目背景

目前征集本题SPJ

USACO 19年一月月赛金组第二题

题目描述

Farmer John正在尝试将他的 NN 头奶牛(1N1051\le N\le 10^5),方便起见编号为 1N1\ldots N,在她们前往牧草地吃早餐之前排好顺序。

当前,这些奶牛以 p1,p2,p3,,pNp_1,p_2,p_3,\ldots,p_N 的顺序排成一行,Farmer John站在奶牛 p1p_1 前面。他想要重新排列这些奶牛,使得她们的顺序变为 1,2,3,,N1,2,3,\ldots,N,奶牛 11 在 Farmer John 旁边。

今天奶牛们有些困倦,所以任何时刻都只有直接面向 Farmer John 的奶牛会注意听 Farmer John 的指令。每一次他可以命令这头奶牛沿着队伍向后移动 kk 步,kk 可以是 11N1N-1 之间的任意数。她经过的 kk 头奶牛会向前移动,腾出空间使得她能够插入到队伍中这些奶牛之后的位置。

例如,假设 N=4N=4,奶牛们开始时是这样的顺序:

 FJ: 4 3 2 1

唯一注意 FJ 指令的奶牛是奶牛 44。当他命令她向队伍后移动 22 步之后,队伍的顺序会变成:

 FJ: 3 2 4 1 

现在唯一注意 FJ 指令的奶牛是奶牛 33,所以第二次他可以给奶牛 33 下命令,如此进行直到奶牛们排好了顺序。

Farmer John 急欲完成排序,这样他就可以回到他的农舍里享用他自己的早餐了。请帮助他求出一个操作序列,使得能够用最少的操作次数将奶牛们排好顺序。

输入格式

输入的第一行包含 NN。第二行包含 NN 个空格分隔的整数:p1,p2,p3,,pNp_1,p_2,p_3,\ldots,p_N,表示奶牛们的起始顺序。

输出格式

输出的第一行包含一个整数 KK,为将奶牛们排好顺序所需的最小操作次数。

第二行包含 KK 个空格分隔的整数,c1,c2,,cKc_1,c_2,\ldots,c_K,每个数均在 1N11\ldots N-1 之间。如果第 ii 次操作 FJ 命令面向他的奶牛向队伍后移动 cic_i 步,那么 KK 次操作过后奶牛们应该排好了顺序。

如果存在多种最优的操作序列,你的程序可以输出其中任何一种。

样例 #1

样例输入 #1
4
1 2 4 3
样例输出 #1
3
2 2 3

提示

参考代码

#include <bits/stdc++.h>
using namespace std;
int n;
int c[1000020];
int a[1000020];
int z[1000020];
int v[1000020];
void change(int x, int y) {
	for (int i = x; i <= n; i += i & -i) {
		c[i] += y;
	}
}
int query(int x) {
	int re = 0;
	for (int i = x; i > 0; i -= i & -i) {
		re += c[i];
	}
	return re;
}
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
		change(a[i], 1);
	}
	int ans = -1;
	for (int i = n - 1; i >= 0; i--) {
		if (i == 0 || a[i - 1] > a[i]) {
			ans = i;
			break;
		}
	}
	printf("%d\n", ans);
	for (int i = ans - 1; i >= 0; i--) {
		change(a[i], -1);
		z[i] = query(a[i]) + ans - 1 - i;
	}
	if (ans == 0) {
		printf("\n");
	} else {
		for (int i = 0; i < ans; i++) {
			printf("%d%c", z[i], i == ans - 1 ? '\n' : ' ');
		}
	}
	return 0;
}

题解

4
1 2 4 3
2 4 1 3
4 1 2 3
1 2 3 4

问最少发出几个命令
发出的命令是什么

注意到没有接收到命令的奶牛(最后若干个奶牛),相对位置不会改变,所以初始必须是有序的

第一问 = n - 最大的升序的长度
the minimum number of time steps = n - the longest sorted suffix

在线
输入一个询问,回答一个询问
往往预处理相关

离线
先读入所有询问,排序……分组……处理这些询问
最后一起回答

如果一个题目在线可以做,离线一定可以做
有的题目在线的做法比离线的做法麻烦非常多
有一些题目强制在线

有的题目离线的做法比在线的做法简单很多

树状数组:
常见错误

  1. 必须从1开始
  2. 修改中的上界,是多少,要考虑清楚

P3660 [USACO17FEB]Why Did the Cow Cross the Road III G

https://www.luogu.com.cn/problem/P3660

题目背景

给定长度为2N的序列,1~N各处现过2次,i第一次出现位置记为ai,第二次记为bi,求满足ai<aj<bi<bj的对数

题目描述

The layout of Farmer John's farm is quite peculiar, with a large circular road running around the perimeter of the main field on which his cows graze during the day. Every morning, the cows cross this road on their way towards the field, and every evening they all cross again as they leave the field and return to the barn.

As we know, cows are creatures of habit, and they each cross the road the same way every day. Each cow crosses into the field at a different point from where she crosses out of the field, and all of these crossing points are distinct from each-other. Farmer John owns NN cows, conveniently identified with the integer IDs 1N1 \ldots N, so there are precisely 2N2N crossing points around the road. Farmer John records these crossing points concisely by scanning around the circle clockwise, writing down the ID of the cow for each crossing point, ultimately forming a sequence with 2N2N numbers in which each number appears exactly twice. He does not record which crossing points are entry points and which are exit points.

Looking at his map of crossing points, Farmer John is curious how many times various pairs of cows might cross paths during the day. He calls a pair of cows (a,b)(a,b) a "crossing" pair if cow aa's path from entry to exit must cross cow bb's path from entry to exit. Please help Farmer John count the total number of crossing pairs.

输入格式

The first line of input contains NN (1N50,0001 \leq N \leq 50,000), and the next 2N2N lines describe the cow IDs for the sequence of entry and exit points around the field.

输出格式

Please print the total number of crossing pairs.

样例 #1

样例输入 #1
4
3
2
4
4
1
3
2
1
样例输出 #1
3

提示

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, x, z;
int c[100020];
pair<int, int> a[50020];
void R(int x, int y)
{
	for (; x <= 2 * n; x += x & -x)
	{
		c[x] += y;
	}
}
int G(int x)
{
	int re = 0;
	for (; x > 0; x -= x & -x)
	{
		re += c[x];
	}
	return re;
}
int main()
{
	cin >> n;
	for (int i = 1; i <= 2 * n; i++)
	{
		cin >> x;
		a[x].first = a[x].second;
		a[x].second = i;
	}
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; i++)
	{
		z += G(a[i].second) - G(a[i].first);
		R(a[i].second, 1);
	}
	cout << z << endl;
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=719

1 ... 2 ... 1 ... 2
sort the intervals, BIT
a[1] = (5, 8)
a[2] = (2, 7)
a[3] = (1, 6)
a[4] = (3, 4)

solution 1:
sort them by the length of interval, decreasing order

for (int i = 0; i < n; i++)
{
ans += query(a[i].right) - query(a[i].left);
change(a[i].left, 1);
change(a[i].right, 1);
}

a[3] = (1, 6)
a[2] = (2, 7)
a[1] = (5, 8)
a[4] = (3, 4)

ans = 0
[0 0 0 0 0 0]0 0 ans += 0
1[0 0 0 0 1 0]0 ans += 1
1 1 0 0[0 1 1 0]ans += 2
1 1[0 0]1 1 1 1 ans += 0
1 1 1 1 1 1 1 1 ans == 3

solution 2:
sort them by the left point

for (int i = 0; i < n; i++)
{
ans += query(a[i].right) - query(a[i].left);
change(a[i].right, 1);
}

a[3] = (1, 6)
a[2] = (2, 7)
a[4] = (3, 4)
a[1] = (5, 8)

ans = 0
[0 0 0 0 0 0]0 0 ans += 0
0[0 0 0 0 1 0]0 ans += 1
0 0[0 0]0 1 0 0 ans += 0
0 0 0 1[0 1 1 0]ans += 2
0 0 0 1 0 1 1 1 ans == 3

生成所有的区间,排序,用树状数组维护部分和

a[i] = make_pair(i第一次出现的位置, i第二次出现的位置)
把i排序

做法一:
1.
按照区间长度,从大到小排序
扫描所有区间
将区间内的和加入答案
将区间的2个端点+=1

把 所有区间 排序,区间长的在前,区间短的在后
bool cmp(pair<int, int> a, pair<int, int> b)
{
return a.second - a.first > b.second - b.first;
}

for (int i = 0; i < n; i++)
{
ans += query(a[i].second) - query(a[i].first);
change(a[i].first, 1);
change(a[i].second, 1);
}

因为先做的是比较长的区间,所以统计答案时不会有包含的情况

做法二:

按照区间左端点排序
扫描所有区间
将区间内的和加入答案
将右端点+=1

区间排序,先排左端点,再排右端点
for (int i = 0; i < n; i++)
{
ans += query(a[i].second) - query(a[i].first);
change(a[i].second, 1);
}

P3372 【模板】线段树 1

https://www.luogu.com.cn/problem/P3372

题目背景

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 kk
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 n,mn, m,分别表示该数列数字的个数和操作的总个数。

第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。

接下来 mm 行每行包含 3344 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 [x,y][x, y] 内每个数加上 kk
  2. 2 x y:输出区间 [x,y][x, y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

样例 #1

样例输入 #1
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
样例输出 #1
11
8
20

提示

对于 30%30\% 的数据:n8n \le 8m10m \le 10
对于 70%70\% 的数据:n103n \le {10}^3m104m \le {10}^4
对于 100%100\% 的数据:1n,m1051 \le n, m \le {10}^5

保证任意时刻数列中任意元素的和在 [263,263)[-2^{63}, 2^{63}) 内。

【样例解释】

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long a[100020];
long long sm[400020];
long long ad[400020];
void build(int x, int l, int r) {
	if (l == r) {
		sm[x] = a[l];
		return;
	}
	int mid = (l + r) / 2;
	build(x * 2, l, mid);
	build(x * 2 + 1, mid + 1, r);
	sm[x] = sm[x * 2] + sm[x * 2 + 1];
}
void add(int x, int l, int r, long long v) {
	sm[x] += (r - l + 1) * v;
	ad[x] += v;
}
void push(int x, int l, int r) {
	int mid = (l + r) / 2;
	add(x * 2, l, mid, ad[x]);
	add(x * 2 + 1, mid + 1, r, ad[x]);
	ad[x] = 0;
}
void change(int x, int l, int r, int L, int R, long long v) {
	if (r < L || R < l) {
		return;
	}
	if (L <= l && r <= R) {
		add(x, l, r, v);
		return;
	}
	push(x, l, r);
	int mid = (l + r) / 2;
	change(x * 2, l, mid, L, R, v);
	change(x * 2 + 1, mid + 1, r, L, R, v);
	sm[x] = sm[x * 2] + sm[x * 2 + 1];
}
long long query(int x, int l, int r, int L, int R) {
	if (r < L || R < l) {
		return 0;
	}
	if (L <= l && r <= R) {
		return sm[x];
	}
	push(x, l, r);
	int mid = (l + r) / 2;
	return query(x * 2, l, mid, L, R) + query(x * 2 + 1, mid + 1, r, L, R);
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	build(1, 1, n);
	for (int i = 0; i < m; i++) {
		int o, x, y;
		long long z;
		scanf("%d", &o);
		if (o == 1) {
			scanf("%d%d%lld", &x, &y, &z);
			change(1, 1, n, x, y, z);
		} else {
			scanf("%d%d", &x, &y);
			printf("%lld\n", query(1, 1, n, x, y));
		}
	}
	return 0;
}

题解

https://www.luogu.com.cn/problem/P3372
http://poj.org/problem?id=3468

  1. 修改单点,区间询问
  2. 修改区间,单点询问
  3. 修改区间,区间询问

差分
b[i] = a[i] - a[i-1]

a[i] = b[1] + b[2] + b[3] + ... + b[i]

询问 a[1] + a[2] + a[3] + ... + a[i]
询问 b[1] * i + b[2] * (i-1) + b[3] * (i-2) + ... + b[i] * 1
询问 (i+1) * (b[1] + b[2] + ... + b[i]) - (b[1] * 1 + b[2] * 2 + ... + b[i] * i)

分别维护 b[i] 和 b[i] * i 的前缀和

P4868 Preprefix sum

https://www.luogu.com.cn/problem/P4868

题目背景

题目描述

前缀和(prefix sum)Si=k=1iakS_i=\sum_{k=1}^i a_k

前前缀和(preprefix sum) 则把SiS_i作为原序列再进行前缀和。记再次求得前缀和第i个是SSiSS_i

给一个长度n的序列a1,a2,,ana_1, a_2, \cdots, a_n,有两种操作:

  1. Modify i x:把aia_i改成xx
  2. Query i:查询SSiSS_i

输入格式

第一行给出两个整数N,M。分别表示序列长度和操作个数
接下来一行有N个数,即给定的序列a1,a2,....an
接下来M行,每行对应一个操作,格式见题目描述

输出格式

对于每个询问操作,输出一行,表示所询问的SSi的值。

样例 #1

样例输入 #1
5 3
1 2 3 4 5
Query 5
Modify 3 2
Query 5
样例输出 #1
35
32

提示

1<=N,M<=100000,且在任意时刻0<=Ai<=100000

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long a[100020];
long long b[100020];
long long sm[400020];
long long ad[400020];
void build(int x, int l, int r) {
	if (l == r) {
		sm[x] = b[l];
		return;
	}
	int mid = (l + r) / 2;
	build(x * 2, l, mid);
	build(x * 2 + 1, mid + 1, r);
	sm[x] = sm[x * 2] + sm[x * 2 + 1];
}
void add(int x, int l, int r, long long v) {
	sm[x] += (r - l + 1) * v;
	ad[x] += v;
}
void push(int x, int l, int r) {
	int mid = (l + r) / 2;
	add(x * 2, l, mid, ad[x]);
	add(x * 2 + 1, mid + 1, r, ad[x]);
	ad[x] = 0;
}
void change(int x, int l, int r, int L, int R, long long v) {
	if (r < L || R < l) {
		return;
	}
	if (L <= l && r <= R) {
		add(x, l, r, v);
		return;
	}
	push(x, l, r);
	int mid = (l + r) / 2;
	change(x * 2, l, mid, L, R, v);
	change(x * 2 + 1, mid + 1, r, L, R, v);
	sm[x] = sm[x * 2] + sm[x * 2 + 1];
}
long long query(int x, int l, int r, int L, int R) {
	if (r < L || R < l) {
		return 0;
	}
	if (L <= l && r <= R) {
		return sm[x];
	}
	push(x, l, r);
	int mid = (l + r) / 2;
	return query(x * 2, l, mid, L, R) + query(x * 2 + 1, mid + 1, r, L, R);
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		b[i] = b[i - 1] + a[i];
	}
	build(1, 1, n);
	for (int i = 0; i < m; i++) {
		char o[9];
		int x, y;
		scanf("%s", o);
		if (*o == 'M') {
			scanf("%d%d", &x, &y);
			change(1, 1, n, x, n, y - a[x]);
			a[x] = y;
		} else {
			scanf("%d", &x);
			printf("%lld\n", query(1, 1, n, 1, x));
		}
	}
	return 0;
}

题解

https://www.luogu.com.cn/problem/P4868

偷懒的做法就是直接贴 线段树1 的代码
和 线段树 1 类似

SS[i] = S[1] + S[2] + ... + S[i]
S[i] = a[1] + a[2] + ... + a[i]
SS[i] = i * a[1] + (i-1) * a[2] + ... + 1 * a[i]
SS[i] = (i + 1) * (a[1] + a[2] + ... + a[i]) - (1 * a[1] + 2 * a[2] + ... + i * a[i])

用两个树状数组,维护 a[i]i*a[i] 的前缀和

P3605 [USACO17JAN]Promotion Counting P

https://www.luogu.com.cn/problem/P3605

题目背景

题目描述

The cows have once again tried to form a startup company, failing to remember from past experience that cows make terrible managers!

The cows, conveniently numbered 1N1 \ldots N (1N100,0001 \leq N \leq 100,000), organize the company as a tree, with cow 1 as the president (the root of the tree). Each cow except the president has a single manager (its "parent" in the tree). Each cow ii has a distinct proficiency rating, p(i)p(i), which describes how good she is at her job. If cow ii is an ancestor (e.g., a manager of a manager of a manager) of cow jj, then we say jj is a subordinate of ii.

Unfortunately, the cows find that it is often the case that a manager has less proficiency than several of her subordinates, in which case the manager should consider promoting some of her subordinates. Your task is to help the cows figure out when this is happening. For each cow ii in the company, please count the number of subordinates jj where p(j)>p(i)p(j) > p(i).

奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者!

为了方便,把奶牛从 1n1\sim n 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点)。除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 “双亲结点”)。

所有的第 ii 头牛都有一个不同的能力指数 pip_i,描述了她对其工作的擅长程度。如果奶牛 ii 是奶牛 jj 的祖先节点,那么我们我们把奶牛 jj 叫做 ii 的下属。

不幸地是,奶牛们发现经常发生一个上司比她的一些下属能力低的情况,在这种情况下,上司应当考虑晋升她的一些下属。你的任务是帮助奶牛弄清楚这是什么时候发生的。简而言之,对于公司的中的每一头奶牛 ii,请计算其下属 jj 的数量满足 pj>pip_j > p_i

输入格式

The first line of input contains NN.

The next NN lines of input contain the proficiency ratings p(1)p(N)p(1) \ldots p(N) for the cows. Each is a distinct integer in the range 11,000,000,0001 \ldots 1,000,000,000.

The next N1N-1 lines describe the manager (parent) for cows 2N2 \ldots N. Recall that cow 1 has no manager, being the president.

输入的第一行包括一个整数 nn

接下来的 nn 行包括奶牛们的能力指数 p1,p2pnp_1,p_2 \dots p_n。保证所有数互不相同。

接下来的 n1n-1 行描述了奶牛 2n2 \sim n 的上司的编号。再次提醒,1 号奶牛作为总裁,没有上司。

输出格式

Please print NN lines of output. The iith line of output should tell the number of subordinates of cow ii with higher proficiency than cow ii.

输出包括 nn 行。输出的第 ii 行应当给出有多少奶牛 ii 的下属比奶牛 ii 能力高。

样例 #1

样例输入 #1
5
804289384
846930887
681692778
714636916
957747794
1
1
2
3
样例输出 #1
2
0
1
0
0

提示

感谢@rushcheyo 的翻译

【数据范围】
对于 100%100\% 的数据,1n1051\le n \le 10^51pi1091 \le p_i \le 10^9

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, ss, x;
vector<int> a[100020];
int p[100020];
int o[100020];
int l[100020];
int r[100020];
int c[100020];
int z[100020];
void dfs(int x)
{
	l[x] = ss;
	for (int i: a[x])
	{
		dfs(i);
	}
	r[x] = ++ss;
}
void change(int x, int y)
{
	for (; x <= n; x += x & -x)
	{
		c[x] += y;
	}
}
int query(int x)
{
	int re = 0;
	for (; x > 0; x -= x & -x)
	{
		re += c[x];
	}
	return re;
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &p[i]);
		o[i] = i;
	}
	sort(o + 1, o + 1 + n, [](int x,int y){return p[x]>p[y];});
	for (int i = 2; i <= n; i++)
	{
		scanf("%d", &x);
		a[x].push_back(i);
	}
	dfs(1);
	for (int i = 1; i <= n; i++)
	{
		z[o[i]] = query(r[o[i]]) - query(l[o[i]]);
		change(r[o[i]], 1);
	}
	for (int i = 1; i <= n; i++)
	{
		printf("%d\n", z[i]);
	}
	return 0;
}

题解

http://usaco.org/index.php?page=viewproblem2&cpid=696

DFS序
DFS order

考虑 这样一棵树,5个点,4条边
1 2
1 3
3 4
3 5

进出栈序
1 2 2 3 4 4 5 5 3 1

1 2 3 4 5

每个数字恰好出现2次
i occurs twice in the sequence

2 types of usages
1.
子树是区间
a subtree is an interval
只在每个点的第一次出现加上点权
only increase at the first time
在一些实现中,我们只存第一次出现
in some implementation
only store the first occurence

欧拉序,每个点被访问一次,就会被记录一次
1 2 1 3 4 3 5 3
总长度2n-2
每个点 出现次数 = 度数
两个点的LCA,就是两个点之间最浅的点

离散化后,相当于每个点的权值是
1 3
2 4
3 1
4 2
5 5

sort these vertices by their values.
5 957747794
2 846930887
1 804289384
4 714636916
3 681692778

5
804289384
846930887
681692778
714636916
957747794
1
1
2
3

1 2 4 4 2 3 5 5 3 1

1 2 4 3 5
[ ] 1
[ ] 2
[ ] 4
[ ] 3
[ ] 5

0 0 0 0[0] z[5] = 0
0[0 0]0 1 a[5] += 1, z[2] = 0
[0 1 0 0 1] a[2] += 1, z[1] = 2
1 1[0]0 1 a[1] += 1, z[4] = 0
1 1 1[0 1] a[4] += 3, z[3] = 1
1 1 1 1 1 a[3] += 1

ans[5] = 0
ans[2] = 0
ans[1] = 2
ans[4] = 0
ans[3] = 1

第一次出现加点权,第二次出现减点权
if inc at the first, dec at the second

1 2 2 3 4 4 5 5 3 1

前缀和一个点到根节点这条链的和
then the prefix is some vectex to the root.

P5094 [USACO04OPEN] MooFest G 加强版

https://www.luogu.com.cn/problem/P5094

题目背景

题目描述

每一年,约翰的 NN 只奶牛参加奶牛狂欢节。这是一个全世界奶牛都参加的大联欢。狂欢节包括很多有趣的活动,比如干草堆叠大赛、跳牛栏大赛,奶牛之间有时还相互扎屁股取乐。当然,她们会排成一列嚎叫,来欢庆她们的节日。奶牛们的叫声实在刺耳,以致于每只奶牛的听力都受到不同程度的损伤。现在告诉你奶牛 ii 的听力为 viv_i ,这表示如果奶牛 jj 想说点什么让她听到,必须用高于 vi×dis(i,j)v_i \times dis(i,j) 的音量。因此,如果奶牛 iijj 想相互交谈,她们的音量必须不小于 max(vi,vj)×dis(i,j)\max (v_i,v_j) \times dis(i,j)。其中 dis(i,j)dis(i,j) 表示她们间的距离。

现在 NN 只奶牛都站在一条直线上了,每只奶牛还有一个坐标 xix_i。如果每对奶牛都在交谈,并且使用最小音量,那所有 N(N1)/2N(N-1)/2 对奶牛间谈话的音量之和为多少?

输入格式

11 行输入一个整数 NN

接下来 NN 行,每行输入两个数 viv_ixix_i ,分别代表第 ii 头奶牛的听力和坐标。

输出格式

输出一个数,代表这 N(N1)/2N(N-1)/2 对奶牛谈话时的音量之和。

样例 #1

样例输入 #1
4
3 1
2 5
2 6
4 3
样例输出 #1
57

提示

数据范围

因为原数据下 O(N2)O(N^2) 算法可以通过,所以新添加了一些增强数据。

原数据作为子任务 11,新添加的数据作为子任务 22

参考代码

#include <bits/stdc++.h>
using namespace std;
int n;
pair<int, int> a[50020];
long long c[50020];
long long d[50020];
long long s, z;
void change(long long *c, int x, int y)
{
	for (; x < 50010; x += x & -x)
	{
		c[x] += y;
	}
}
long long query(long long *c, int x)
{
	long long re = 0;
	for (; x > 0; x -= x & -x)
	{
		re += c[x];
	}
	return re;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d%d", &a[i].first, &a[i].second);
	}
	sort(a, a + n);
	for (int i = 0; i < n; i++)
	{
		z += (s - 2 * query(d, a[i].second) - (i - 2 * query(c, a[i].second)) * a[i].second) * a[i].first;
		s += a[i].second;
		change(c, a[i].second, 1);
		change(d, a[i].second, a[i].second);
	}
	printf("%lld\n", z);
	return 0;
}

题解

https://vjudge.net/problem/POJ-1990

排序所有的牛,按音量升序
sort all cows by their volume threshold increasing order

for (int i = 0; i < n; i++)
{
ans += (the sum of all distances)* volume threshold[i];
}

距离有2种可能,左边和右边

4
2 5
2 6
3 1
4 3

num 0 0 0 0 0 0
sum 0 0 0 0 0 0
answer = 0
num 0 0 0 0 1 0
sum 0 0 0 0 5 0
answer = 0 + (1 * 6 - 5) * 2 = 2
num 0 0 0 0 1 1
sum 0 0 0 0 5 6
answer = 2 + ((5 + 6) - 2 * 1) * 3 = 29
num 1 0 0 0 1 1
sum 1 0 0 0 5 6
answer = 29 + (1 * 3 - 1) * 4 + ((5 + 6) - 2 * 3) * 4 = 57
num 1 0 1 0 1 1
sum 1 0 3 0 5 6

左边:
(the number of cows on our left * a[i].x - the sum of x of the cows on our left)

右边:
(the sum of x of the cows on our right - the number of cows on our right * a[i].x)

加在一起
(the number of cows on our left - the number of cows on our right) * a[i].x
+
(the sum of x of the cows on our right - the sum of x of the cows on our left)

(左边的个数 - (总数 - 左边的个数)) * a[i].x
+
((总坐标之和 - 左边的坐标之和) - 左边的坐标之和)

(2 * 左边的个数 - 总数) * a[i].x + (总坐标之和 - 2 * 左边的坐标之和)

https://vjudge.net/problem/POJ-1195

https://vjudge.net/problem/POJ-2155

https://vjudge.net/problem/POJ-2352
输入已经排序好了(y升序,y相等的话按x升序)

对x轴维护一个树状数组

对于每个点(x, y):
询问树状数组中 <= x 位置的和(这些点在自己的左下方
树状数组的x位置++

一些特殊情况
在这个题目中,给出X, Y
询问有多少个点x[i] <= X && y[i] <= Y

如果是
x[i] < X && y[i] < Y
x[i] <= X && y[i] < Y
x[i] < X && y[i] <= Y
x[i] <= X && y[i] <= Y

分别应该怎么做?
如果是x[i] < X,那么在树状数组询问 < x的位置 的和(而不是 <= x)
如果是y[i] < Y,那么如果y相等,按x降序

如果点重合怎么办?(没考虑

https://vjudge.net/problem/POJ-2828
https://www.cnblogs.com/ZhaoxiCheung/p/5782487.html

poj 2828 Buy Tickets

http://poj.org/problem?id=2828

倒序考虑,询问第kk11

倒序考虑所有人
如果插入在了第pos个人的后面,相当于问第pos+1个1是谁?
把自己放在这个位置,把1改成0

Buy Tickets

Find the k-th 1 in the orginal array a[].
找第k个1
We assume that there are only 0 and 1 in the array a[]
假设a数组中只有和0和1
Find the index of x-th 1 in the array a[]
问a数组中第k个1是什么?

直接二分,既然x是第k个1,那么一定满足query(x) >= k && query(x-1) < k
Use binary search
Because x is the index of the k-th 1 in the array,
it must satisfy query(x) >= k and query(x-1) < k

(x == re + 1)

int ask(int x) // 第x个1
{
    int re = 0; // 保证任何时候 query(re) < x
    for (int i = 1 << 20; i > 0; i /= 2)
    {
        if (re + i <= n && x > c[re + i]) // 如果 query(re + i)之后还是<x
        {
            re += i; // 把 re 改成 re + i
            x -= c[re];
        }
    }
    // re is the largest, which satisfy query(re) < x
    // query(re + 1) >= x
    return re + 1;
}

c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4]
c[5] = a[5]
c[6] = a[5] + a[6]
c[7] = a[7]
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]

We hope 希望
a[1] + a[2] + a[3] + ... + a[re] < x
a[1] + a[2] + a[3] + ... + a[re] + a[re + 1] >= x

Tips
1. BIT must be used from 1, not zero, because lowbit(0) is 0
1. 树状数组必须从1开始,不能从0开始
2. The English name of 树状数组 is Fenwick Tree or BIT
2. Fenwick tree / binary indexed tree / BIT
3. The update operation only supports increment not assignment.
   If assignment is needed, we need to maintain the initial array a[].
3. 修改操作只支持a[x] += y,如果需要a[x] = y需要额外维护数组a
4. bisides addition, BIT supports other operators, such as xor (Exclusive OR). 
   the operators generally need an inverse operator.
4. 树状数组 支持的操作 一般是需要可逆

常见的可逆操作
Common reversible operations

+ // addition
+ mod // addition with mod
^ (相当于,很多维的数字,每一维求和mod2)
xor // n-dimension of addition mod 2
前缀乘积(?)
prefix product // multipilcation has an inverse operator, but it seldomly be used.


For a special type of dynamic programming, we use BIT to maintain the maximum.
有一种特殊的动态规划,可以用树状数组维护最大值
1. change, only change smaller a[x] to larger
1. 修改,只会把小的a[x]改大
2. query prefix only
2. 询问只有前缀

另一种方法:注意到单调性,用map来维护
Another solution: use map

Based on the intial array a[], construct the BIT c[], it need O(n) instead of O(n log n)
根据数组a,初始化树状数组c,不需要O(n log n)的时间

for (int i = 1; i <= n; i++)
{
    c[i] = a[i];
}
for (int i = 1; i <= n; i++)
{
    if (i + (i & -i) <= n)
    {
        c[i + (i & -i)] += c[i];
    }
}

prefix sum and difference

s[i] = a[1] + a[2] + .. + a[i] = s[i-1] + a[i]

b[i] = a[i] - a[i-1]


interval change 
point query

1. change l, r, x. a[l] += x, a[l+1] += x, ... , a[r] += x;
2. query x; return x

interval change only changes 2 positions of the difference array

a[l] += x, a[l+1] += x, ... , a[r] += x;
b[l] += x; b[r+1] -= x; // only 2 positions

     0 0 0 0 0 0  0 0 0 0
     0 0 1 1 1 1  0 0 0 0 a
diff 0 0 1 0 0 0 -1 0 0 0 b

2-D BIT

a[i][j]
c[i][j] = sum(a[i-lowbit(i)+1 .. i][j-lowbit(j)+1 .. j])
void change(int x, int y, int z)
{
// a[x][y] += z;
for (int i = x; i <= n; i += i & -i)
{
for (int j = y; j <= n; j += j & -j)
{
c[i][j] += z;
}
}
}

int query(int x, int y)
{
int re = 0;
for (int i = x; i > 0; i -= i & -i)
{
for (int j = y; j > 0; j -= j & -j)
{
re += a[i][j];
}
}
return re;
}

3-D and 2-D are very similar
The more Demension, the higher the time complexity.

P2345 [USACO04OPEN]MooFest G

https://www.luogu.com.cn/problem/P2345

for (int i = 0; i < n; i++)
{
a[i].first = volume;
a[i].second = position;
}
sort(a, a + n); // sort cows in volume increasing order
for (int i = 0; i < n; i++)
{
ans += a[i].first * ((number of cows in front of us) * a[i].second - (sum of x in front of us))
ans += a[i].first * ((sum of x behind us) - (number of cows behind us) * a[i].second)
}

P5142 区间方差

方差 = 平方的平均数 - 平均数的平方
a[]的方差 = (a[1]^2 + a[2]^2 + ... + a[n]^2) / n -
((a[1] + a[2] + ... + a[n]) / n)^2

1和3的方差 = (1 + 9) / 2 - ((1 + 3) / 2)^2 = 1

change(c1, x, -a[x]);
change(c2, x, -a[x]*a[x]);
a[x] = y;
change(c1, x, +a[x]);
change(c2, x, +a[x]*a[x]);

change(c1, x, y-a[x]);
change(c2, x, y*y-a[x]*a[x]);
a[x] = y

乘法逆元:模质数的情况下

  1. 模p意义下,x 的逆元 pow(x, p - 2, p)
    x * pow(x, p - 2, p) % p == 1
    (费马小定理)

inv(1) = 1
inv(x) = (p-p/x) * inv(p % x) % p

Error: ENOENT: no such file or directory, open '/Users/wwwwodddd/Dropbox/Informatics/problems/P3801'

poj 2155

poj 2155 Matrix
二维的差分
2D difference

change time complexity: O(log^2 n)

a[i][j] = sum of b[1..i][1..j]

b[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]

For this problem, we use BIT to maintain b[i][j]

when we change x1 <= x <= x2, y1 <= y <= y2, we change
b[x1][y1]
b[x1][y2+1]
b[x2+1][y1]
b[x2+1][y2+1]

修改 x1 <= x <= x2, y1 <= y <= y2
相当于修改
x2++;
y2++;
change(x1, y1, 1);
change(x1, y2, 1);
change(x2, y1, 1);
change(x2, y2, 1);

void change(int x, int y, int z)
{
for (int i = x; i <= n; i += i & -i)
{
for (int j = y; j <= n; j += j & -j)
{
c[i][j] += z;
}
}
}

void query(int x, int y, int z)
{
int re = 0;
for (int i = x; i > 0; i -= i & -i)
{
for (int j = y; j > 0; j -= j & -j)
{
re += c[i][j];
}
}
return re;
}

poj 1195 Mobile phones

必须从1开始下标

poj 2352 Stars

2D problem, sort 1D, use BIT to solve another D

排序所有点(输入已经是有序的了)
sort all points (the input is sorted)
如果先按y从小到大,y相同按x从小到大排序

ans[query(x)]++;
change(x, 1);

x可能有0,需要++
现在题目中问的是 xi <= x, yi <= y
改成 xi < x, yi <= y 怎么做?
ans[query(x - 1)]++;
change(x, 1);

改成 xi <= x, yi < y 怎么做?
排序需要注意,
先按y从小到大,y相同按x从大到小排序

改成 xi < x, yi < y 怎么做?
排序需要注意,
先按y从小到大,y相同按x从大到小排序
ans[query(x - 1)]++;
change(x, 1);

对于所有问题都需要特别注意有没有重合的(x, y)

P3605 [USACO17JAN]Promotion Counting P

DFS
按(能力,下标)排序

扫描所有奶牛
询问自己子树的和(比自己能力高的一定先加入)
把自己改成1

二维偏序:排序 树状数组 / 排序 线段树 / 排序 cdq分治(类似归并排序)
三维偏序:排序 cdq分治 树状数组
四维偏序:排序 cdq分治套cdq分治 树状数组

1 2 4 3
2 4 1 3
4 1 2 3
1 2 3 4

c[i] = a[i - lowbit(i) + 1, .., i]
c[i][j] = a[i - lowbit(i) + 1, .., i][j - lowbit(j) + 1, .., j]

DFS序
1 2 2 3 4 4 5 5 3 1
1 2 3 4 5

每一个子树都是连续的一段,可以用树状数组维护区间和

2 1

前i-1个数字有多少个 > i-1
前i个数字有多少个 > i = i - (前i项有几个<=i的)

答案为什么会发生变化?
第i个数字>i,答案++
前i-1个数字中有i,答案--

线段树数组开多大?
4n 或者 8n(矩形面积并的错误写法,不要在叶子节点update)

大小为n的线段树,需要空间是 大于等于n的最小的2的次幂 *2

哪些信息可以合并

求和,异或
乘积
min, max, gcd
最大的若干个
矩阵乘法
DP转移

标记和自己无关(所以叶子节点可能有标记,但是完全没有用)

什么样的东西可以当做标记
标记必须可以合并
区间加一个值 可以
区间加一个等差数列 可以
区间加一个公比相同的等比数列 可以
等比数列可以是矩阵的等比数列,比如说Fibonacci数
区间加一个公比不相同的等比数列 不可以

264
2
32
216
2
8
24
2
2
2**1

https://loj.ac/problem/135

P4514 上帝造题的七分钟

https://www.luogu.com.cn/problem/P4514

题目背景

裸体就意味着身体。

题目描述

“第一分钟,X 说,要有矩阵,于是便有了一个里面写满了 00n×mn\times m 矩阵。

第二分钟,L 说,要能修改,于是便有了将左上角为 (a,b)(a,b),右下角为 (c,d)(c,d) 的一个矩形区域内的全部数字加上一个值的操作。

第三分钟,k 说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作。

第四分钟,彩虹喵说,要基于二叉树的数据结构,于是便有了数据范围。

第五分钟,和雪说,要有耐心,于是便有了时间限制。

第六分钟,吃钢琴男说,要省点事,于是便有了保证运算过程中及最终结果均不超过 3232 位有符号整数类型的表示范围的限制。

第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。”。

——《上帝造裸题的七分钟》。

所以这个神圣的任务就交给你了。

输入格式

输入数据的第一行为 X n m,代表矩阵大小为 n×mn\times m
从输入数据的第二行开始到文件尾的每一行会出现以下两种操作:

请注意,kk 为小写。

输出格式

针对每个 kk 操作,在单独的一行输出答案。

样例 #1

样例输入 #1
X 4 4
L 1 1 3 3 2
L 2 2 4 4 1
k 2 2 3 3
样例输出 #1
12

提示

对于 10%10\% 的数据,1n161 \le n \le 161m161 \le m \le 16, 操作不超过 200200 个。

对于 60%60\% 的数据,1n5121 \le n \le 5121m5121 \le m \le 512

对于 100%100\% 的数据,1n20481 \le n \le 20481m20481 \le m \le 2048500delta500-500 \le delta \le 500,操作不超过 2×1052\times 10^5 个,保证运算过程中及最终结果均不超过 3232 位带符号整数类型的表示范围。

参考代码

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[2049][2049];
int b[2049][2049];
int c[2049][2049];
int d[2049][2049];
void change(int x, int y, int z)
{
	for (int i = x; i <= n; i += i & -i)
	{
		for (int j = y; j <= m; j += j & -j)
		{
			a[i][j] += z;
			b[i][j] += z * x;
			c[i][j] += z * y;
			d[i][j] += z * x * y;
		}
	}
}
int query(int x, int y)
{
	int ra = 0, rb = 0, rc = 0, rd = 0;
	for (int i = x; i > 0; i -= i & -i)
	{
		for (int j = y; j > 0; j -= j & -j)
		{
			ra += a[i][j];
			rb += b[i][j];
			rc += c[i][j];
			rd += d[i][j];
		}
	}
	return ra * (x + 1) * (y + 1) - rb * (y + 1) - rc * (x + 1) + rd;
}
int main()
{
	scanf("X %d %d", &n, &m);
	for (char o; ~scanf(" %c", &o);)
	{
		int x1, y1, x2, y2, z;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		if (o == 'L')
		{
			scanf("%d", &z);
			change(x1, y1, z);
			change(x1, y2 + 1, -z);
			change(x2 + 1, y1, -z);
			change(x2 + 1, y2 + 1, z);
		}
		else
		{
			printf("%d\n", query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1));
		}
	}
	return 0;
}

题解

ans =
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
a[i][j]

a is 2D prefix sum of b
b is the difference array of a

a[i][j] =
for (int k = 1; k <= i; k++)
for (int l = 1; l <= j; l++)
b[k][l]

ans =
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
for (int k = 1; k <= i; k++)
for (int l = 1; l <= j; l++)
b[k][l]

ans =
for (int k = 1; k <= n; k++)
for (int l = 1; l <= m; l++)
for (int i = k; i <= n; i++)
for (int j = l; j <= n; j++)
b[k][l]

ans =
for (int k = 1; k <= n; k++)
for (int l = 1; l <= m; l++)
b[k][l] * (n - k + 1) * (m - l + 1)

into 4 parts
b[k][l]
b[k][l] * k
b[k][l] * l
b[k][l] * k * l


s[i] = a[1] + a[2] + ... + a[i]

s[1] + s[2] + ... + s[x] =
x*a[1] + (x-1)*a[2] + (x-2)a[3] + ... + 1a[x]

(x+1-i) * a[i]

(x+1) * (a[1] + a[2] + ... + a[x]) -
(1a[1] + 2a[2] + ... + x * a[x])

数组a
array a
数组b是数组a的前缀和
array b is the prefix sum of array a
数组c是数组b的前缀和
array c is the prefix sum of array b
数组d是数组c的前缀和
array d is the prefix sum of array c

修改a[x] = y
change a[x] = y
问d数组中的一项d[x]
query d[x]

d[x] =
for (int i = 1; i <= x; i++)
c[i]

d[x] =
for (int i = 1; i <= x; i++)
for (int j = 1; j <= i; j++)
b[j]

d[x] =
for (int i = 1; i <= x; i++)
for (int j = 1; j <= i; j++)
for (int k = 1; k <= j; k++)
a[k]

d[x] =
for (int k = 1; k <= j; k++)
a[k] * (the number of i, j such that k <= j <= i <= x)

d[x] =
for (int k = 1; k <= j; k++)
a[k] * (x + 1 - k) * (x + 2 - k) / 2

into 3 parts
c[k]
c[k] * k
c[k] * k * k

poj 3468的二维版本。
据说有CDQ分治相关的方法。

P2344 [USACO11FEB]Generic Cow Protests G

前缀和

s[i] = a[1] + a[2] + .. + a[i]

s[0] = 0

s[0] s[1] s[2] ... s[n]
求有多少个不下降的子序列,s[0]和s[n]必须选择

s[0] s[1] s[3] s[4]
s[0] <= s[1] <= s[3] <= s[4]
(a[1]) (a[2] a[3]) (a[4])

f[0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < i; j++)
{
if (s[i] >= s[j])
{
f[i] += f[j];
}
}
}
printf("%d\n", f[n]);

(i, s[i])

线段树 扫描线
Luogu P5490

这些楼里能看到几个
最高的一个楼是什么

P6098

https://www.luogu.com.cn/problem/P6098

DFS序
DFS序有两种
进出栈序
1 2 2 3 4 4 5 5 3 1

欧拉序
1 2 1 3 4 3 5 3

用来和树状数组配合的是进出栈序
有两种使用方法

修改单点,询问子树和
把点权放在第一次出现的位置上
子树和就是区间和

修改单点,询问根节点到某节点的路径和
在第一次出现的位置上加上点权
在第二次出现的位置上减去点权
1 2 2 3 4 4 5 5 3 1

实际实现的时候,可以第二次出现位置不+1
0 1 2 3 4 5 6
1 1
2 2
3 3
4 4
5 5

0 1 2 3 4 5 6 7 8 9 10
1 1
2 2
3 3
4 4
5 5

abc185_f

https://atcoder.jp/contests/abc185/tasks/abc185_f
异或树状数组

CF1660F2

https://codeforces.com/problemset/problem/1660/F2

spoj DEV_CP

https://www.spoj.com/problems/DEV_CP/

线段树

应用广泛的一个数据结构。

要求支持区间加法。(区间合并)

实现

和堆类似,需要使用用数组存二叉树的技巧。
ii节点的左右孩子分别为2i,2i+12i, 2i+1
每个节点表示一个区间,常见的写法有闭区间[l, r]或者是左闭右开[l, r)

建树

void build(int x, int L, int R) {
    //左闭右开[L, R)
    if (L == R - 1) {
        mn[x] = a[L];
        return;
    }
    build(x * 2, L, M);
    build(x * 2 + 1, M, R);
    mn[x] = min(mn[x * 2], mn[x * 2 + 1]);
}

修改

void change(int x, int L, int R, int p, int v) {
    if (L == R - 1) {
        mn[x] = v;
        return;
    }
    int M = (L + R) / 2;
    if (p < M) {
        change(2 * x, L, M, p, v);
    } else {
        change(2 * x + 1, M, R, p, v);
    }
}

询问

void query(int x, int L, int R, int l, int r) {

}

打标记

要求标记可以合并,标记与当前节点无关。常见的标记有

  1. 染色/区间修改为同一个值
  2. 区间内每个数字xkx+bx \to kx + b
  3. 区间加等差数列/多项式
  4. 区间加公比相同的等比数列
  5. 区间加Fibonacci数等可以看做矩阵等比数列的东西。

矩形面积并

因为有复杂度大概是4n24n^2的方法,所以很多题并不需要线段树。

可持久化线段树

可以部分替代划分树和归并树等数据结构。

并且更加易于实现,易于调试。

树链剖分

对于树上的问题,可以进一步使用树链剖分解决。

统计的力量

zkw线段树。

和树状数组对比

树状数组的优势在于易于推广到高维,易于实现,常数小。

线段树的优势在于功能强大,只需要支持合并操作即可,可以打标记,可以可持久化。

参考题目

poj 1151
矩形面积并,不需要线段树。

bzoj 1798
维护序列,支持区间加法和乘法。

Luogu P4513
小白逛公园,经典最大子区间和。

Luogu 280D
可以选kk个的最大子区间和。

Luogu P4145
区间开根号,每个数字会被操作的次数非常少。

bzoj 2957
楼房重建,每次合并区间,需要再在左子树询问一遍。

codechef DGCD
先树链剖分,区间加一个数,区间求gcd。
差分之后转化为 区间修改单点查询 和 单点修改区间询问gcd。

参考资料

《统计的力量》

线段树

segment tree
线段树

  1. 区间如何表示? how to represent an interval
    [l, r) 左闭右开 left closed right open
    [l, r] 闭区间 closed

  2. 二叉树怎么存 how to store the binary tree

x's children are 2*x and 2*x+1, the root is 1
x的孩子是2*x和2*x+1,根节点是1

x's children are 2*x+1 and 2*x+2, the root is 0
x的孩子是2*x+1和2*x+2,根节点是0

(Implementation)
We don't store the right and left endpoints
比较主流的写法:结构体不记录区间左右端点,如果需要通过线段树传下去
void 函数(int 当前节点标号, int 区间左端点,int 区间右端点, ...)
void add/change/query(int current, int left_endpoint, int right_endpoint, int left_query, int right_query)
{
if (left_endpoint > right_query || right_endpoint < left_query) // disjoint
{
return 0;
}
if ()
{
return the information of current node.
}
return query(left ..) + query(right ..)
}

void add/change/query(int current, int left_endpoint, int right_endpoint, int left_query, int right_query)
{
if ()
}

How large should the array be? 4n
空间?开4n

2 * (the smallest power of 2) >= n
稍微精确一点的结果:2 * 大于等于n的最小的2的次幂

n = 100000, 131072

mx[x] = max(mx[x], v);

P3372, P3373, P4145, P4513

P3372 【模板】线段树 1
P3373 【模板】线段树 2
P4513
P4145 上帝造题的七分钟2

线段树
Segment Tree

树状数组是只有左孩子的线段树
BIT is Segment Tree with left children only.

线段树不需要区间相减,只需要区间合并
Segment Tree don't need subtraction, but addition (merge)

可以合并的信息 (data which can be merged)
最大值 maximum
次大值 second maximum
最小值 minimum
区间和 sum
出现次数超过一半的众数 majority number

不可以合并的信息 (data which can not be merged)
区间中位数 (median)
区间第k大 (k-th largest)
区间众数 (mode number)

线段树建树保证
节点个数 O(n)
节点总长度 O(n log n)
任意一个区间都可以被 O(log n) 个节点覆盖
任何一个点只被 O(log n) 个节点覆盖

RMQ / Range minimum query
区间最小值

  1. Segment Tree / 线段树
    空间 Space O(n)
    预处理时间 preprocess time O(n)
    求最小值 query O(log n)
    可以支持修改 change O(log n)

ST表

  1. 笛卡尔树 LCA

RMQ 通过 笛卡尔树 转化为 LCA
LCA 通过 DFS序 转化为 RMQ

最优复杂度都是 O(n) 预处理 O(1) 回答

https://www.spoj.com/problems/RMQSQ/
basic RMQ

https://www.spoj.com/problems/KGSS/
find the largest two in the intervals

http://www.spoj.com/problems/GSS1
http://www.spoj.com/problems/GSS3
https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P4513.cpp

4 information
维护4个信息

sum      the sum
sum      总和
leftmax  the interval with max sum, starts at the left point(prefix)
leftmax  从最左开始的最大子段和
rightmax the interval with max sum, ends at the right point(suffix)
rightmax 从最左开始的最大子段和
max      the interval with max sum
max      最大子段和


sm[x] = sm[x*2] + sm[x*2+1]
x的和 等于 x左子树的和 + x右子树的和

lmx[x] = max(lmx[x*2], sm[x*2] + lmx[x*2+1])
x的左最大 等于 x左子树的左最大 和 x左子树的和+x右子树的左最大 中的较大值

rmx[x] = max(rmx[x*2+1], sm[x*2+1] + rmx[x*2])
x的右最大 等于 x右子树的右最大 和 x右子树的和+x左子树的右最大 中的较大值

mx[x] = max(max(mx[x*2], mx[x*2+1]), rmx[x*2] + lmx[2*x+1])
最大子段和 在左子树 在右子树 左子树的右最大+右子树的左最大

convert LCA to RMQ
use dfs order

1 2 2 3 4 4 5 5 3 1 / push pop order
1 2 1 3 4 3 5 3
to query LCA
we want to find the one with least depth.

LCA(2, 4)
min(2, 1, 3, 4) which has the least depth
make_pair(d[x], x)

+-1 RMQ
the preprocess time can be optimize to O(n)
abs(a[i]-a[i-1]) <= 1

convert RMQ to LCA
Cartesian tree

http://poj.org/problem?id=3468
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
// 1 2 6 7 8 9 7 8 9 10
Q 2 4

1 2 3 4 5 6 7 8 9 10
1 1 1 1 1 1 1 1 1 1    b[k]
1 2 3 4 5 6 7 8 9 10   k * b[k]

1 2 6 7 8 9 7 8 9 10
1 1 4 1 1 1 -2 1 1 1
1 2 12 4 5 6 -14 8 9 10

1+2+6+7+8+9 = 7*(1+1+4+1+1+1) - (1+2+12+4+5+6)

difference

a[1] a[2] a[3] .. a[n]
a[0] = 0

b[i] = a[i] - a[i-1]
a[i] = b[1] + b[2] + .. + b[i]

a[1] + a[2] + .. + a[i] = i * b[1] + (i-1) * b[2] + (i-2) * b[3] + ... + 1 * b[i]

a1+a2++ai=j=1iaj=j=1ik=1jbk=k=1i(ik+1)bk=ib1+(i1)b2++1bia_1 + a_2 + \cdots + a_i = \sum_{j=1}^{i} a_j = \sum_{j=1}^{i} \sum_{k=1}^j b_k = \sum_{k=1}^{i} (i-k+1)b_k = ib_1 + (i-1)b_2 + \cdots + 1b_i

k=1i(ik+1)bk=(i+1)(k=1ibk)(k=1ikbk)\sum_{k=1}^{i} (i-k+1)b_k = (i+1)(\sum_{k=1}^{i}b_k) - (\sum_{k=1}^{i}kb_k)

We need two BIT to maintain the prefix sum of bkb_k and kbkk b_k.
https://www.luogu.com.cn/blog/Start-Dash/P3372

The lazy tag has nothing to do with itself
标记和自己无关

The lazy tag must be able to be merged.
标记必须可以合并

常见可以合并的标记
mergeable:

  1. 区间 += d,区间增加
  2. addition (add the same value)
  3. 区间 *= d,区间乘法
  4. multiplication
  5. 区间 = d,区间赋值
  6. assignment
  7. 区间翻转,0变成1,1变成0
  8. change 0 to 1, change 1 to 0
  9. 区间 += 等差数列,a[l] += 1, a[l+1] += 2, a[l+2] += 3, ...
  10. add arithmetic sequence (any common differences)
  11. 公比相同的等比数列,等比数列是广义的等比数列,比如Fibonacci也算等比数列
  12. geometric sequence (the same common ratio)
  13. 区间加法 区间乘法
  14. addition and multiplication (change x to k x + b)

x -> k1 x + b1
x -> k2 x + b2

x -> (k2 (k1 x + b1) + b2)
x -> k1 k2 x + (k2 b1 + b2)

1x + 0

x

不可以合并的标记
non-mergeable

  1. 公比不同的等比数列
  2. geometric sequence (different common ratio)

The lazy tags on leafs is useless.
it has nothing to do with itself, and can't be pushed down.
叶子节点的标记是没有用的,因为标记和自己无关,标记没有办法继续向下推

leafs and the query interval are either disjoint or contained.
对于叶子节点,和询问区间的关系,要么相离,要么包含

For some simple lazy tags, some implementations don't push them down.
对于一些简单的标记,有不推标记的写法(可以忽略)

区间先+2,再+3,相当于+5
区间先2,再3,相当于*6

能不能既支持加法,又支持乘法?

顺序问题
区间先+2,再2 x->2x+4
区间先
2,再+2 x->2x+2

标记:k x + b 记录k和b

k2(k1 x + b1) + b2
k1k2 x + (b1 k2 + b2)

https://www.luogu.com.cn/problem/P3373
5 5 38
1 5 4 2 3
2 1 4 1 // 2 6 5 3 3
3 2 5 // 6+5+3+3 = 17
1 2 4 2 // 2 12 10 6 3
2 3 5 5 // 2 12 15 11 8
3 1 4 // 2+12+15+11 = 40%38 = 2

https://www.spoj.com/problems/GSS4/
https://www.luogu.com.cn/problemnew/solution/P4145
(Another solution: USE union find set to jump 1s, and use BIT to maintain the sum)
5
1 2 3 4 5
5
1 2 4 // 9
0 2 4 // 1 1 1 2 5
1 2 4 // 4
0 4 5 // 1 1 1 1 2
1 1 5 // 6

2**1 开1次根号变到1 sqrt(2**1) = 1
2**2 开2次根号变到1 sqrt(sqrt(2**2)) = 1
2**4 开3次根号变到1 sqrt(sqrt(sqrt(2**4))) = 1
2**8 开4次根号变到1 sqrt(sqrt(sqrt(sqrt(2**8)))) = 1
2**16 开5次根号变到1 sqrt(sqrt(sqrt(sqrt(sqrt(2**16))))) = 1
2**32 开6次根号变到1 sqrt(sqrt(sqrt(sqrt(sqrt(sqrt(2**32)))))) = 1
2**64 开7次根号变到1  sqrt(sqrt(sqrt(sqrt(sqrt(sqrt(sqrt(2**64))))))) = 1
2**64 > 1e18

sqrt(x) = x
void change(int x, int l, int r, int L, int R)
{
if (s[x] == r - l + 1)
{
return;
}
}

http://usaco.org/index.php?page=viewproblem2&cpid=865

LIS
longest increasing subsequence

1 4 7 2 5 8 3 6 9

  1. use binary search

f[i] = the minimum of the last one of some LIS with length i
f[i] is increasing
when a new element added, f[i] changes at most 1 postion

0 x x x x x x
0 1 x x x x x
0 1 4 x x x x
0 1 4 7 x x x
0 1 2 7 x x x
0 1 2 5 x x x
0 1 2 5 8 x x
0 1 2 3 8 x x
0 1 2 3 6 x x
0 1 2 3 6 9 x

length of LIS is 5

https://github.com/wwwwodddd/Zukunft/blob/master/luogu/P1020.cpp

Another important application:
use the minimum number of increasing sequences to cover a sequence /
divide the given sequence into minimum number of increasing sequences

the answer is the length of longest decreasing sequence.
because any two numbers in the longest decreasing sequence can not be in the same increasing sequence

  1. use data structure

f[i] = the longest LIS which the last element is i

read x
f[x] = max(f[1], f[2] ... f[x]) + 1
we will use BIT or segtree.
BIT

  1. only change smaller to larger.
  2. only query prefix

next problem:
what is the number of Longest Increasing Subsequences
https://www.cnblogs.com/DOLFAMINGO/p/8719525.html

f[i] = (the length of the longest LIS which the last element is i, the number of ...)

If the lengths are the same, sum the number of plans up.
If the lengths are different, choose the longest one.

We want delete the k-th smallest subset == the k-th largest subset remains.
Find the k-th largest LIS.

For each element, start with this element, the length / number of plans of LIS.

3 2 1 6 5 4 9 8 7

7: (length 1, plans: 1)
8: (length 1, plans: 1)
9: (length 1, plans: 1)
4: (length 2, plans: 3)
5: (length 2, plans: 3)
6: (length 2, plans: 3)
1: (length 3, plans: 9)
2: (length 3, plans: 9)
3: (length 3, plans: 9)

If one element is in the LIS, then its position is unique.
find the 14-th largest LIS

3 ? NO. 14 -= 9 -> 5
2 ? YES. 5 <= 9

6 ? NO. 5 -= 3 -> 2
5 ? YES. 2 <= 3

9 ? NO. 2 -= 1 -> 1
8 ? YES. 1 <= 1

14-th largest LIS. 2 5 8
https://www.luogu.com.cn/blog/Mirach/solution-p5156

#include <bits/stdc++.h>
typedef long long ll;

inline void read(int&x){
    char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
}

const int N=101000;
struct Edge{int v,nxt;}e[N];
int a[N],chs[N],head[N];
int n,_;ll k;

const ll lim=1e18;

struct node{
    int v;ll c;
    // v is length
    // c is the number of plans
    inline node(){}
    friend inline void operator + (node&A,const node B){
    	// v
        if(A.v<B.v)A.v=B.v,A.c=B.c;
        else if(A.v==B.v)A.c=std::min(lim,A.c+B.c);
    }
}d[N],g[N],cl;

#define lb(x) (x&(-x))

inline node qy(int x){node p=cl;for(x;x<=n+1;x+=lb(x))p+d[x];return p;}
// query is special, x += lowbit(x)
inline void add(int x,node y){for(;x;x-=lb(x))d[x]+y;}
// add is special, x -= lowbit(x)
inline void ae(int u,int v)
{e[++_].v=v,e[_].nxt=head[u],head[u]=_;}
// adjcent list

int main(){
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;++i)read(a[i]);
    cl.c=1,add(n+1,cl),cl.c=0;
    for(int i=n;i;--i){ // from last one to the first one
        g[i]=qy(a[i]);  // query suffix the one after a[i] must be >= a[i]
        ++g[i].v; // increase the length by 1
        add(a[i],g[i]); // add it back
    }
    for(int i=n;i;--i) // backward
    	ae(g[i].v,i); // i is the value
    	// the last added, is the first visied
    // vector[length].push_back(index);
    // qy(1).v is the length of LIS
    for(int stp=qy(1).v,R=0;stp;--stp) // for length from max to 1
    {
	    for(int i=head[stp],v;i;i=e[i].nxt){ // try every possible elements.
	    	// be careful about the order.
	        v=e[i].v;
	        // e[i].v means value, the value is index
	        // v is the index
	        if(g[v].c<k)
	        {
	        	// the number of plans are not enough
	        	k-=g[v].c;
	        }
	        else
	        {
	        	// it's enough, try next length.
	        	// v is index
	            chs[a[v]]=true;
	            // v is the index, a[v] is the value
	            // a[v] should not be printed
	            while(R<v)
	            {
	            	g[R++]=cl;
	            	// cl means clear
	            	// clear all postions before v // the next one must after v
	            }
	            break;
	        }
	    }
    }

    printf("%d\n",n-qy(1).v);
    // n - the length of LIS
    for(int i=1;i<=n;++i)
        if(!chs[i])printf("%d\n",i);
        // output iff chs[i]==0
    return 0;
}

https://www.luogu.com.cn/blog/milkfilling/solution-p3373

P1471 方差
不止维护一次方和,还维护0次方和,2次方和。

方差 = 平方的平均数 - 平均数的平方

区间长度 s0
区间和 s1
区间平方和 s2
区间立方和 s3
区间加k之后,上面3个值是怎么变的?

new s0 = s0
new s1 = s1 + s0 * k
new s2 = s2 + 2 s1 * k + s0 * k * k
new s3 = s3 + 3 s2 * k + 3 * s1 * k * k + s0 * k * k * k

1 * C(n, 1) + 15 * C(n, 2) + 50 * C(n, 3) + 60 * C(n, 4) + 24 * C(n, 5)

2 ** 64 -> 2 ** 32 -> 2 ** 16 -> 2 ** 8 -> 2 ** 4 -> 2 ** 2 -> 2 ** 1 -> 1

a[i]

b[i] = a[1] + a[2] + ... + a[i]
c[n] = b[1] + b[2] + ... + b[n]
c[n] = na[1] + (n-1)a[2] + ... + 1a[n]
c[n] = (n+1)
(a[1] + a[2] + a[3] + .. + a[n]) - (1a[1] + 2a[2] + 3a[3] + ...na[n])

P4513 小白逛公园
SP1043 GSS1 - Can you answer these queries I
SP1716 GSS3 - Can you answer these queries III
SP2916 GSS5 - Can you answer these queries V

P5156 [USACO08FEB]Hotel G
每个子树 / 线段树的中间节点,
维护
从左边向右最多连续多少个0
从右边向左最多连续多少个0
当前子树最长一段全是0的区间是多长

懒惰标记
区间内是不是全是 0 或 1

P3097 [USACO13DEC]Optimal Milking G

1 2 3 4 5

1 2 3 4 2
2+4 = 6
1 7 3 4 2
7+4 = 11
10 7 3 4 2
10+3+2 = 15

必须不选最左边的 && 必须不选最右边的 0
必须不选最左边的 && 必须选最右边的 1
必须选最左边的 && 必须不选最右边的 2
必须选最左边的 && 必须选最右边的 3

f[x][0] = max(f[2 * x][0] + f[2 * x + 1][0], f[2 * x][0] + f[2 * x + 1][2], f[2 * x][1] + f[2 * x + 1][0])

f[x][i] = max()
f[2 * x][i & 2] + f[2 * x + 1][i & 1] // 左子树最右 和 右子树最左都不选
f[2 * x][i & 2] + f[2 * x + 1][i | 2] // 左子树最右 不选 和 右子树最左 选
f[2 * x][i | 1] + f[2 * x + 1][i & 1] // 左子树最右 选 和 右子树最左 不选

必须不选最左边的 && 必须不选最右边的
必须不选最左边的 && 最右边的选不选都可以
最左边的选不选都可以 && 必须不选最右边的
最左边的选不选都可以 && 最右边的选不选都可以

f[x][3] = max(f[x][3], f[x][2], f[x][1], f[x][0])
f[x][2] = max(f[x][2], f[x][0])
f[x][1] = max(f[x][1], f[x][0])

21 开1次根号变到1
2
2 开2次根号变到1
24 开3次根号变到1
2
8 开4次根号变到1
216 开5次根号变到1
2
32 开6次根号变到1
2**64 开7次根号变到1

P5490 【模板】扫描线
扫描线,线段树求矩形面积并

矩形面积并的线段树不推标记

P6098 [USACO19FEB]Cow Land G
修改一个点的点权
询问一个点到根节点路径上所有点权的xor
询问一个点的点权

DFS序 和 树状数组/线段树

  1. 问一个点到根节点的权值之和
    进栈的位置 +=
    出栈的位置 -=
    询问x到根节点的情况,问 开始 到 x进栈的位置

  2. 问一个点的子树之和
    进栈的位置 += 1

问子树求和
http://poj.org/problem?id=3321 Apple Tree
子树在DFS序中是连续的一段
进出栈DFS序

1 2 3 4 5
1 1
22
3 3
44
55
1 2 2 3 4 4 5 5 3 1
长度为2n,每个点恰好出现2次

http://usaco.org/index.php?page=viewproblem2&cpid=624

Eulerian Path

http://usaco.org/index.php?page=viewproblem2&cpid=816
Slingshots

divide into 4 parts
use segtree/BIT/map to find the max/min of prefix/suffix

https://www.luogu.com.cn/problem/P6119
[USACO17FEB]Why Did the Cow Cross the Road II G

LCS longest common subsequence
special case:
the LCS of two permutations is the same as LIS

3 2 1 6 5 4
2 1 4 3 6 5
change the number to the number
1 2 3 4 5 6
2 3 6 1 4 5
for the second line, any increasing subsequence is a common subsequence.
Time Complexity is O(n log n)

another special case:
any number occurs at most twice.
a[]: 1 1 2 2 3 3
b[]: 1 2 3 1 2 3

(1, 1) // a[1] == b[1]
(2, 1) // a[2] == b[1]
(1, 4)
(2, 4)
(3, 2)
(4, 2)
(3, 5)
(4, 5)
(5, 3)
(6, 3)
(5, 6)
(6, 6)

sort these points by x, if x is the same, greater y comes first
bool cmp(Point a, Point b)
{
return a.x < b.x || (a.x == b.x && a.y > b.y);
}

(1, 4)
(1, 1)
(2, 4)
(2, 1)
(3, 5)
(3, 2)
(4, 5)
(4, 2)
(5, 6)
(5, 3)
(6, 6)
(6, 3)

find the longest increasing (not non-decreasing) subsequence, which is the length of LCS.

[USACO11DEC]Grass Planting G

in a tree
change a path, query a vertex is the same as
change a vertex, query a subtree // use DFS order and BIT

change a subtree, query a vertex is the same as
change a vertex, query a path // use DFS order and BIT

http://usaco.org/index.php?page=viewproblem2&cpid=722
4 3 2 1 1 4 2 3

2D version
sort 1D, use BIT/segtree for 1D

3D version
sort 1D, use 2D segtree
CDQ D&C

change one position, query the sum

sort them
(first_occur[x], second_occur[x], x)

4 1

4
3
2
1

1
4
2
3

(1, 2, 4)
(2, 4, 3)
(3, 3, 2)
(4, 1, 1)

https://www.luogu.com.cn/blog/27-43wyy/solution-p3658

// query friendly crossings

// the (friendly) inversion caused by (4 and something) sum(d[2..n][4-k..4+k]) = 0
// the inversion caused by (4 and something) sum(d[2..n][...]) = 0
change(2, 4, 1) // d[2][4] += 1

// the (friendly) caused by (4 and something) sum(d[4..n][3-k..3+k]) = 0
// the inversion caused by (4 and something) sum(d[4..n][...]) = 0
change(4, 3, 1) // d[4][3] += 1

// the (friendly) caused by (4 and something) sum(d[3..n][2-k..2+k]) = 1
// the inversion caused by (4 and something) sum(d[3..n][...]) = 1
change(3, 2, 1) // d[3][2] += 1

// the (friendly) caused by (4 and something) sum(d[1..n][1-k..1+k]) = 1
// the inversion caused by (4 and something) sum(d[1..n][...]) = 3
// 2 unfriendly crossings
change(1, 1, 1) // d[1][1] += 1

for (int i = 0; i < n; i++)
{
change(second_occur[a[i].second], a[i].third, 1);
query()
}

https://www.luogu.com.cn/problem/P1531
最基础的线段树

单点修改
区间最大

spoj SPAM_ATX

https://www.spoj.com/problems/SPAM_ATX/

栈 (stack)

  1. 表达式求值
  2. 括号匹配 (1种括号,只需要计数,多种需要栈)
  3. 单调栈 (求凸包,求最大矩形)

中缀表达式:需要括号
后缀表达式
8 3 2 6 * + 5 / - 4 +
遇到数值入栈
遇到符号,拿出需要的操作数(一般是2),进行运算,放回去
最后应该剩下恰好一个数字。

8 3 2 6
8 3 12
8 15
8 15 5
8 3
5
5 4
9

CF81A Plug-in

https://codeforces.com/problemset/problem/81/A
输入一个字符串,把所有连续相同的2个字符删掉,问最后剩下什么。

Luogu P1165 日志分析

https://www.luogu.com.cn/problem/P1165
维护一个栈,支持加入,删除,询问栈中的最大值。

Luogu P1449 后缀表达式

https://www.luogu.com.cn/problem/P1449
输入一个后缀表达式,求值。

Luogu P5788 【模板】单调栈

https://www.luogu.com.cn/problem/P5788
输入一个数字,对于每个位置i,找到i之后第一个大于a[i]的位置。

agc005_a STring

https://atcoder.jp/contests/agc005/tasks/agc005_a
输入一个字符串,如果ST是这个字符串的子串,删去这个子串,直到ST不是这个字符串的子串。

stack:

  1. 表达式求值
  2. 括号匹配 (1种括号,只需要计数,多种需要栈)
  3. 单调栈 (求凸包,求最大矩形)

中缀表达式
需要括号

后缀表达式
8 3 2 6*+5/-4+
遇到数值入栈
遇到符号,拿出需要的操作数(一般是2),进行运算,放回去
最后应该剩下恰好一个数字。

维护一个数值栈
8 3 2 6
8 3 12
8 15
8 15 5
8 3
5
5 4
9

2类做法

  1. O(n^2) 直接递归
    找到最后一个执行的运算是什么
    然后两边递归处理

按运算符优先级枚举,先枚举低优先级的(先枚举加减,再乘除,再乘方)
根据运算符结合性,如果加法这种的,从右向左扫
如果是乘方,从左向右
括号内的不算
特殊情况整体被一个括号包住了
(a + b + c)

a + b + c = (a + b) + c
a ** b ** c = a ** (b ** c)


  1. 需要一个数值栈
    需要一个符号栈

优先级小的可以让优先级大的运算
优先级相等(取决于结合性)
如果没有括号,这样做就可以了

a + b * c *

数值栈:a b c
符号栈:+ *

括号的处理
任何运算都不能让左括号(出栈,括号优先级最低
只有右括号遇到左括号会消掉
(a + b)

Stack

LIFO

  1. expression evaluation
  2. Catalan Number
  3. Monotonic Stack

The number of paths from (0, 0) to (n, n) without touching (y = x + 1) =

The number of paths from (0, 0) to (n, n) - The number of paths from (0, 0) to (n, n) touching (y = x + 1)

C(2n, n) - C(2n, n-1) = C(2n,n) / (n+1)

http://oeis.org/A000108

(a + b) mod p = ((a mod p) + (b mod p)) mod p

(a- b) mod p = ((a mod p) - (b mod p)) mod p

(a * b) mod p = ((a mod p) * (b mod p)) mod p

(a / b) mod p = ((a mod p) * inv(b mod p)) mod p

2 * 1/2 = 1

x / 2 = x * (1/2)

2 * 4 mod 7 = 1

x / 2 mod 7 = x * 4 mod 7

Divided (1, 2, 3, .., n) to some sets.

The sets can not intersect with each other.

(1, 3) (2, 4)

(1, 4) (2) (3)

stated-compressed DP

P3668 [USACO17OPEN]Modern Art 2 G

1 2 1 2

vector we can access the i-th element.

stackwe can only access the top. we need to access the top two of the stack.

queue we can only access the front.

P6182 [USACO10OPEN]Time Travel S

The operations formed a tree.

名称

栈(stack)是一种后进先出(LIFO, last in first out)的数据结构,这是一种非常基础的数据结构,在很多地方,尤其是DFS和单调栈中有广泛的应用。

实现

可以使用C++ STL中的stack实现,也可以自己模拟。

通常用一个数组和一个指针模拟一个栈,数组用于存储元素,指针top表示栈顶,入队时将top向后移动,出队时将top向前移动。

栈并不需要支持访问任意元素,所以在某些情况下会用链表模拟栈。(比如NOIP初赛)

具体使用

栈的用途比队列多一些。

表达式求值

凸包/凸壳

可以用来求凸包,但是因为凸包需要访问最后22个元素,用以判断折线的方向,所以不能用STL的Stack

单调栈

可以用来解决最大矩形面积的问题。

递归

递归算法都会用到栈,只不过系统会自动维护。
如果想使用非递归算法,那么就需要自己实现栈了。

参考题目

Luogu P1175
表达式求值

Luogu P5155
凸壳

Luogu P1044
出栈序列数目,Catalan数。

POJ 1964
最大矩形面积

POJ 2559
最大矩形面积

Luogu P739
判断括号是否匹配

Luogu P1449
后缀表达式求值

Luogu P1981
中缀表达式求值

Luogu P2947
用栈

Luogu P2866
用栈

队列

名称

队列(queue)是一种先进先出(FIFO, first in first out)的数据结构,这是一种非常基础的数据结构,在很多地方,尤其是BFS和单调队列中有广泛的应用。

实现

可以使用C++ STL中的queue实现
https://zh.cppreference.com/w/cpp/container/queue
也可以自己模拟

通常用一个数组和两个指针模拟一个队列,数组用于存储元素,指针front和back分别表示队首和队尾,入队时将back向后移动,出队时将front向后移动。

队列并不需要支持访问任意元素,所以在某些情况下会用链表模拟队列。(比如NOIP初赛)

为了节约空间还可以使用循环队列,即将整个队列看做一个环,位置x的下一个位置是(x + 1) % size,虽然这一般并没有用,因为如果会MLE,基本也会TLE。

应用

队列在BFS(搜索)和图论相关的算法中有广泛的应用。

值得注意的是,一般所说的单调队列,实际上并不是队列,而是双端队列(一端进,两端出)

参考题目

全部BFS的题目都需要使用队列。

Luogu P1090
用堆进行贪心,可以优化成队列。

Luogu P2827
用堆进行贪心,需要优化成队列以卡常数。

队列 Queue

BFS求最短路 BFS to shortest path

https://www.luogu.com.cn/problem/P1334

https://www.luogu.com.cn/problem/P1090

https://www.luogu.com.cn/problem/P6033

https://en.wikipedia.org/wiki/Huffman_coding

[USACO06NOV]Fence Repair G

use priority_queue

Merge the smallest two, every time

sort the n integers.

sort:Introsort

use merge sort to count the number of inversions.

单调栈 Monotonic Stack

最大矩形

Largest Rectangle

http://poj.org/problem?id=2559

use a stack to store the rectangles (width, height)

the height is increasing

https://www.spoj.com/problems/HISTOGRA/

单调队列

P1886,P2627,P3029,P3069,P4085,P5202

P1886 滑动窗口 /【模板】单调队列

区间互不包含的最值问题
these intervals can not contain each other.

[1, 5]
[2, 3]
算作包含

[1, 5] contains [2, 3]

[1, 5]
[1, 3]
端点重合不算包含

[1, 5] does not contain [1, 3]

O(n + q)
数组长度 + 询问数
length of array + number of queries

双端队列
double-ended queue

一端进,两端出
push elements to the end of this queue
pop elements from both ends

最小值
for example, minimum

队列单调严格增加的
this queue should be strictly increasing

每次加入一个数字
each time we push an element

while (队列非空,队尾 > 当前数字)
删去队尾

while (queue is not empty, queue.back >= current element)
{
queue.pop()
}

[b, f)
b 队首
f 队尾,入队 q[f++] = i;
队列的大小 f - b
if (b < f) 那么队列非空

if (队列的第一个太早了)
{
删掉队列的第一个
}
队列中一定要存元素下标,而不是值

#include <bits/stdc++.h>
using namespace std;
int a[1000020];
int q[1000020], b, f;
int n, m;
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    b = f = 0;
    for (int i = 1; i <= n; i++)
    {
        while (b < f && a[q[f - 1]] >= a[i])
        {
            f--;
        }
        q[f++] = i;
        while (q[b] <= i - m)
        {
            b++;
        }
        if (i >= m)
        {
            printf("%d%c", a[q[b]], i == n ? '\n' : ' ');
        }
    }
    b = f = 0;
    for (int i = 1; i <= n; i++)
    {
        while (b < f && a[q[f - 1]] <= a[i])
        {
            f--;
        }
        q[f++] = i;
        while (q[b] <= i - m)
        {
            b++;
        }
        if (i >= m)
        {
            printf("%d%c", a[q[b]], i == n ? '\n' : ' ');
        }
    }
}

P3029 [USACO11NOV]Cow Lineup S
P3069 [USACO13JAN]Cow Lineup G
P4085 [USACO17DEC]Haybale Feast G
P5202 [USACO19JAN]Redistricting P

P2698 [USACO12MAR]Flowerpot S

P2949 [USACO09OPEN]Work Scheduling G
priority queue to do greedy algorithm

P2947 [USACO09MAR]Look Up S
monotonic stack

from 1 to n
decreasing stack, stack used from 1
for (int i = 1; i <= n; i++)
{
while (ss > 0 && h[s[ss]] < h[i])
{
answer[s[ss--]] = i;
}
s[++ss] = i;
}

from n downto 1
decreasing stack, stack used from 1
for (int i = n; i > 0; i--) {
while (ss > 0 && h[s[ss]] <= h[i]) {
ss--;
}
z[i] = s[ss];
s[++ss] = i;
}

doubly linked list
to delete i
for (int i = 0; i < n; i++)
{
// do them in {p[i]} order
R[L[p[i]]] = R[p[i]]
L[R[p[i]]] = L[p[i]]
// R[p[i]] is the answer of p[i]
}

P3088 [USACO13NOV]Crowded Cows S

#include <bits/stdc++.h>
using namespace std;
pair<int, int> a[50020];
int ql[50020], bl, fl;
int qr[50020], br, fr;
int n, d, z;
int main()
{
    scanf("%d%d", &n, &d);
    for (int i = 0; i < n; i++)
    {
        scanf("%d%d", &a[i].first, &a[i].second);
    }
    sort(a, a + n);
    for (int i = 0, jl = 0, jr = 0; i < n; i++)
    {
        // jl 左边区间[ a[i].first - d, a[i].first ] 枚举到第几个牛了 (只会加一个)
        while (jl < n && a[jl].first <= a[i].first)
        {
            // 第 jl 个牛在左边区间内,加入左边区间
            while (bl < fl && a[ql[fl - 1]].second <= a[jl].second)
            {
                fl--;
            }
            ql[fl++] = jl++;
        }
        // jl 左边区间[ a[i].first, a[i].first + d ] 枚举到第几个牛了 (可能加多个)
        while (jr < n && a[jr].first <= a[i].first + d)
        {
            // 第 jr 个牛,在右边区间,加入右边区间
            while (br < fr && a[qr[fr - 1]].second <= a[jr].second)
            {
                fr--;
            }
            qr[fr++] = jr++;
        }
        // 如果 左边区间队首的牛已经 不在 [ a[i].first - d, a[i].first ]
        // 那么就删掉(可能删多个)
        while (a[ql[bl]].first < a[i].first - d)
        {
            bl++;
        }
        // 如果 右边区间队首的牛已经 不在 [ a[i].first, a[i].first + d]
        // 那么就删掉(只会删一个)
        while (a[qr[br]].first < a[i].first)
        {
            br++;
        }
        // min(左边最大, 右边最大) >= 自己的二倍
        if (min(a[qr[br]].second, a[ql[bl]].second) >= a[i].second * 2)
        {
            z++;
        }
    }
    printf("%d\n", z);
    return 0;
}
min(x, y) >= h
等价于
x >= h && y >= h

P1419 寻找段落
P1440 求m区间内的最小值
P1714 切蛋糕
P1725 琪露诺
P2032 扫描
P3957 跳房子

monotonic queue

knapsack problem

  1. 01 knapsack
    for (size downto 0)
  2. infinite knapsack
    for (0 to size)
  3. finite knapsack
    2 solutions
  4. binary 二进制拆分

100 item, weight w, value v
we can do 100 times 01 knapsack

void update(int w, int v)
{
for (int j = totalsize; j >= w; j--)
{
f[j] = max(f[j], f[j - w] + v);
}
}

for (int k = 0; k < 100; k++)
{
update(w, v);
}
too slow

100 = 1 + 2 + 4 + 8 + 16 + 32 + 37

50 = 2 + 16 + 32
50 = 1 + 4 + 8 + 37

update(w * 1, v * 1);
update(w * 2, v * 2);
update(w * 4, v * 4);
update(w * 8, v * 8);
update(w * 16, v * 16);
update(w * 32, v * 32);
update(w * 37, v * 37);

limit = 100;
for (int k = 1; k <= limit; k *= 2)
{
update(w * k, v * k);
limit -= k;
}
update(w * limit, v * limit);

P4544 [USACO10NOV]Buying Feed G

f[i][j] min cost at position i, j units of feed.
if there is no shop at i,
f[i][j] = f[i-1][j] + j * j;

if there is a shop at i,
we should do knapsack on f[i]

  1. monotonic queue

do weight times monotonic queue, each with length (size/weight)

weight = 2
value = v
limit = 3;

f[13] = max(f[13], f[11] + v, f[9] + v * 2, f[7] + v * 3)
f[11] = max(f[11], f[ 9] + v, f[7] + v * 2, f[5] + v * 3)
f[9]  = max(f[ 9], f[ 7] + v, f[5] + v * 2, f[3] + v * 3)
f[7]  = max(f[ 7], f[ 5] + v, f[3] + v * 2, f[1] + v * 3)

f[13] = max(f[13]-6*v, f[11]-5*v, f[9]-4*v, f[7]-3*v) + 6*v;
f[11] = max(f[11]-5*v, f[ 9]-4*v, f[7]-3*v, f[5]-2*v) + 5*v;
f[9]  = max(f[ 9]-4*v, f[ 7]-3*v, f[5]-2*v, f[3]-1*v) + 4*v;
f[7]  = max(f[ 7]-3*v, f[ 5]-2*v, f[3]-1*v, f[1]-0*v) + 3*v;

f[1]-0*v, f[3]-1*v, f[5]-2*v, f[7]-3*v, f[9]-4*v, f[11]-5*v, f[13]-6*v
f[0]-0*v, f[2]-1*v, f[4]-2*v, f[6]-3*v, f[8]-4*v, f[10]-5*v, f[12]-6*v

P2627 / P2034

P2627 [USACO11OPEN]Mowing the Lawn G
P2034 选择数字
https://www.luogu.com.cn/problem/P2034
转化为选一些位置,不能有连续k+1个位置不选,最小化选取的和
choose some elements, minimize their sum.
the distance between two adjacent elements are at most k

a[0] = 0
a[n+1] = 0

位置 0 和 位置 n+1 一定选择
we must choose a[0] and a[n+1]

f[i] 选择了第 i 个位置,最小和是多少
f[i] if we choose i, the minimum sum is f[i]
答案是 sum - f[n+1]
答案是 sum - min(f[n], f[n-1], ..., f[n-m])
the answer is sum - f[n+1]
the answer is sum - min(f[n], f[n-1], ..., f[n-m])

f[i] = min(f[i-1], f[i-2], ... f[i-m-1]) + a[i]

当k=2时
when k = 2
f[3] = min(f[2], f[1], f[0]) + a[3] = 3
f[6] = min(f[5], f[4], f[3]) + a[6] = 3

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long s;
int a[100020];
long long f[100020];
int q[100020], bb, ff;
int main()
{
    cin >> n >> m;
    q[ff++] = 0; // f[0] = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        s += a[i];
        f[i] = f[q[bb]] + a[i];
        while (bb < ff && f[q[ff - 1]] >= f[i])
        {
            ff--;
        }
        q[ff++] = i;
        if (q[bb] < i - m)
        {
            bb++;
        }
    }
    cout << s - f[q[bb]] << endl;
    return 0;
}

P5202 [USACO19JAN]Redistricting P

下标从1开始
每个区最大包含 m 个字符

g[i] 表示前i个划区 G >= H 最少几个区?
g[i] for the first i pastures, the minimum number of distrcts (G >= H)

s[i] 表示 前i个, H 的个数 - G 的个数
s[i] for the first i pastures, the number of H - the number of G

g[i-m] g[i-m+1] ... g[i-1] -> g[i]
从哪个位置转移最好呢?

首先 f 越小越好,如果 f 一样,s 越小越好
如果 f 小了,后面的 s 最差情况下,也就是 + 1

决定从 i - j 转移 (1 <= j <= m)

g[i] = min(g[i], g[i - j] + (s[i] - s[i - j] <= 0))

如果 s[i] <= s[i - j] 就意味着从 i-j+1, i-j+2, ..., i 这些共j个数字中,H 的个数 <= G 的个数

找到最小的 (g[i-j], s[i-j]) (1 <= j <= m)
minimze the pair (g[i-j], s[i-j]) (1 <= j <= m)

#include <bits/stdc++.h>
using namespace std;
int n, m, b, f;
int q[300020];
int g[300020];
int s[300020];
char c[300020];
int main() {
    scanf("%d%d%s", &n, &m, c);
    for (int i = 0; i < n; i++) {
        s[i + 1] = s[i] + (c[i] == 'H' ? 1 : -1);
    }
    q[f++] = 0; // (g[0], s[0])
    for (int i = 1; i <= n; i++) {
        while (q[b] < i - m) {
            b++;
        }
        // 从 q[b] 转移到 i
        g[i] = g[q[b]] + (s[i] - s[q[b]] <= 0);
        // 新状态 (g[i], s[i]) 入队
        while (b < f && make_pair(g[q[f - 1]], s[q[f - 1]]) >= make_pair(g[i], s[i])) {
            f--;
        }
        q[f++] = i;
    }
    printf("%d\n", g[n]);
    return 0;
}

单调队列 优化 混合背包

P1714 切蛋糕
求前缀和 s[i] 前 i 个数字的和

枚举区间的右端点 i

如果左端点是 j, 那么区间长度是 i - j
区间的和 a[j+1] + a[j+2] + ... + a[i] = s[i] - s[j]

s[i] - s[j] (i - m <= j < i)

#include <bits/stdc++.h>
using namespace std;
int n, m, z = -2e9;
int s[500020];
int q[500020], b, f;
int main()
{
    cin >> n >> m;
    q[f++] = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i];
        s[i] += s[i - 1];
        // 删除 < i - m 的部分
        while (b < f && q[b] < i - m)
        {
            b++;
        }
        // s[i] - min(s[i-1], s[i-2], ..., s[i-m]);
        // 得到的就是以 a[i] 为最后一个元素的最优解
        z = max(z, s[i] - s[q[b]]);
        // s[i] 加入队列
        while (b < f && s[q[f - 1]] > s[i])
        {
            f--;
        }
        q[f++] = i;
    }
    cout << z << endl;
    return 0;
}

P1419 寻找段落
P1440 求m区间内的最小值
P1725 琪露诺
P2032 扫描
P5202 [USACO19JAN]Redistricting P

P3957 跳房子

花的金币越多
选择越多
得分越高

第一步,二分花多少金币
第二个,给定金币个数,计算最高得分

#include <bits/stdc++.h>
using namespace std;
int n, d, k;
int x[500020];
int s[500020];
int q[500020], b, f;
long long g[500020];
long long calc(int M) { // M 个金币最高得分
    int L = max(1, d - M);
    int R = d + M;
    // 跳跃范围 [L, R]
    // 想跳到 x[i] 起点的范围是 x[i] - R <= x[j] <= x[i] - L
    long long re = 0;
    b = f = 0;
    for (int i = 1, j = 0; i <= n; i++) {
        // 如果 x[j] <= x[i] - L 那么就加入单调队列
        while (j <= n && x[i] - x[j] >= L) {
            while (b < f && g[j] > g[q[f - 1]]) {
                f--;
            }
            q[f++] = j++;
            // 即使 g[j] 是不合法的状态-1e18,也会被加入单调队列用来更新其他节点
        }
        // 当 队首 < x[i] - R 就 删掉
        while (b < f && x[i] - x[q[b]] > R) {
            b++;
        }
        if (b < f) {
            g[i] = g[q[b]] + s[i];
        } else {
            g[i] = -1e18; // 起点到不了当前状态,当前状态不合法
        }
        re = max(re, g[i]); // 找所有终点的最大值
    }
    return re;
}
int main() {
    scanf("%d%d%d", &n, &d, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x[i], &s[i]);
    }
    int L = -1, R = 1000000007;
    while (L < R - 1) {
        int M = (L + R) / 2;
        if (calc(M) >= k) {
            R = M;
        } else {
            L = M;
        }
    }
    if (R == 1000000007) {
        printf("-1\n");
    } else {
        printf("%d\n", R);
    }
    return 0;
}

Monotonic Queue

http://poj.org/problem?id=2823

RMQ with intervals not including each other, we can use Monotonic Queue.

Redistricting

f[i] is the best answer of the first i letters

f[i] = min(f[j] + (s[i]-s[j]>=0), (i-j)<=k)

j+1,j+2,..,i

s[i] = (the number of G - the number of H) in the first i letters.

f[j1] == f[j2]

Find j (i-k<=j<=i-1) minimize (f[j], -s[j]) 


P2627 [USACO11OPENMowing the Lawn G

Use monotonic queue to optimize DP.

f[i] = the minimum sum if a[i] is deleted.

f[i] = min(f[j], i - k - 1<= j < i) + a[i]

f[0] = 0

a[0] = 0

a[n+1] = 0

f[n+1]

单调队列

简介

可以在O(n+q)O(n + q)的时间内,解决询问不互相包含的区间最值问题。

算法流程

将所有询问区间排序,从左向右依次处理。

以poj 2823为例

#include <bits/stdc++.h>
using namespace std;
int a[1000020];
int q[1000020];
// 队列q中存的是数组下标
int n, k, b, f;
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    b = f = 0;
    for (int i = 0; i < k - 1; i++) {
       // 依次加入前k-1个元素
        while (b < f && a[q[f - 1]] >= a[i]) {
            f--;
        }
        // 如果新加入的元素a[i]比队尾的元素还小
        // 那么队尾的元素不再可能成为最小值,直接删去即可
        q[f++] = i;
        // 元素a[i]入队
    }
    for (int i = k - 1; i < n; i++) {
        if (q[b] <= i - k) {
            b++;
        }
        // 如果队首的元素位置过早,删去他;这里至多删一次,使用if即可
        while (b < f && a[q[f - 1]] >= a[i]) {
            f--;
        }
        // 如果新加入的元素a[i]比队尾的元素还小
        // 那么队尾的元素不再可能成为最小值,直接删去即可
        q[f++] = i;
        // 元素a[i]入队
        printf("%d%c", a[q[b]], i == n - 1 ? '\n' : ' ');
        // 当前的队首即为最小值
    }
    b = f = 0;
    for (int i = 0; i < k - 1; i++) {
        while (b < f && a[q[f - 1]] <= a[i]) {
            f--;
        }
        q[f++] = i;
    }
    for (int i = k - 1; i < n; i++) {
        if (q[b] <= i - k) {
            b++;
        }
        while (b < f && a[q[f - 1]] <= a[i]) {
            f--;
        }
        q[f++] = i;
        printf("%d%c", a[q[b]], i == n - 1 ? '\n' : ' ');
    }
}

参考题目

Luogu P1440
经典单调队列

poj 2823
经典单调队列

Luogu P3957
二分,单调队列

spoj FESTIVAL

https://www.spoj.com/problems/FESTIVAL/

spoj ADASQR

参考资料

树链剖分

简单介绍

一些针对树上一条链的题目,可以通过树链剖分解决。

核心思想是将树分成若干条链,每条链建一棵线段树,对于书上一条路径的操作,
转化为对于每个链的操作。在此基础上,使得一条路径跨过的链的个数尽量少。

轻重链剖分

每个点向自己最大的子树连一条边,这样整个树可以被划分成叶子数条链。

在一些实现中,最大的子树(节点数最多)也可以被实现为叶子数最多的子树。
之后的分析没有变化。

时间复杂度

可以发现如果顺着一条非重链向上跳,那么子树大小至少乘以22

这样从任意一个点跳到根节点,至多需要跳O(logn)O(\log n)次,
也就是说任意两个点之间的路径至多跨过O(logn)O(\log n)个重链。
每条重链再用线段树维护,总时间复杂度为O(log2n)O(\log^2 n)

参考题目

Luogu P2146
非常好的一道题,将DFS序和树链剖分结合了起来。

bzoj 1036
树链剖分模板题。

树链剖分

把一个树划分成若干个链
每个点属于且只属于一条链
任意两点之间,路径至多经过log(n)个链
怎么划分最好?

长链,最坏情况 sqrt(n)
重链,最坏情况 log(n) 每个点计算重儿子

第一次DFS,计算出每个子树的大小

第二次DFS,生成DFS序
DFS时每到一个点,先走重儿子

P4377 [USACO18OPEN]Talent Show G

f[i] 表示重量i,最大的才艺值
最大化f[i] / i,只枚举i >= W
重量范围太大,不行

f[i] 表示才艺值i,最小的重量
最大化i / f[i],只枚举f[i] >= W
是错误的

2 3
2 1
1 1
1 1000

01分数规划
二分最终答案
每个物品只有1种属性 才艺值-重量 * 最终答案
背包,判断是否存在总重量 >= W
属性和 >= 0

P1774 最接近神的人
(2, 1)
(8, 2)
(0, 3)
(3, 4)

(0, 3)
(2, 1)
(3, 4)
(8, 2)

#include <bits/stdc++.h>
using namespace std;
int n;
int a[100020];
int b[100020];
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);
		b[i] = i;
	}
	sort(b + 1, b + 1 + n, [](int x, int y){return a[x] < a[y];});
	for (int i = 1; i <= n; i++)
	{
		printf("%d%c", b[i], i == n ? '\n' : ' ');
	}
}

P1396 营救
二分时,可以只二分出现过的权值

CF609E
https://codeforces.com/contest/609/submission/14889101
每个集合维护一个边集,表示边的一端在集合内,另一端在集合外

合并两个集合的时候,维护这个边集
回答边的两端在这两个集合中
路径压缩

https://codeforces.com/contest/609/submission/42482642
不路径压缩
启发式合并,得到一个树高不太大的树
在启发式合并得到的树上暴力进行询问

树链剖分
Heavy Light Decomposition

https://www.luogu.com.cn/problem/P2590
[ZJOI2008]树的统计

https://www.luogu.com.cn/problem/P2486
[SDOI2011]染色

https://www.luogu.com.cn/problem/P3178
[HAOI2015]树上操作
子树加,询问到根节点的一条链,可以用DFS序和线段树
x子树中每个点都 += y

询问x子树中每个点i的答案(从i到根节点的和) += (d[i] - d[x] + 1) * y
询问x子树中每个点i的答案(从i到根节点的和) += d[i] * y - (d[x] - 1) * y
对于每个点维护2个部分,比如a和b
a[i] += y
b[i] += (d[x] - 1) * y
最终答案是a[i] * d[i] - b[i]

子树修改,单点求值
DFS序,转化为单点修改,区间求和

链加,子树求和,可以用DFS序和线段树

x到根节点每个点i都 += y

询问x到根节点每个点i的答案(点i子树的和) += (d[x] - d[i] + 1) * y
询问x到根节点每个点i的答案(点i子树的和) += (d[x] + 1) * y - d[i] * y

对于每个点维护2个部分,比如a和b
a[i] += y
b[i] += (d[x] + 1) * y
最终答案是b[i] - a[i] * d[i]

链修改,单点求值
DFS序,转化为单点修改,区间求和

4个问题

  1. 子树增加,询问子树
    区间增加,询问区间

  2. 子树增加,询问链

  3. 链增加,询问子树

  4. 链增加,询问链

1 2
1 3
3 4
3 5

进出栈序列
1 2 2 3 4 4 5 5 3 1

询问4 =

修改4 =
修改+ w[1] + w[2] + w[3] + w[4]
修改- w[2]

转化为了2个修改前缀(前缀增加一个数字

https://www.luogu.com.cn/problem/P2146
[NOI2015]软件包管理器

https://www.luogu.com.cn/problem/P3384
【模板】轻重链剖分

https://www.luogu.com.cn/problem/P3313
[SDOI2014]旅行
动态开点?
每个点用一个map?
set<pair<int, int> >

每个点维护一个 数据结构
支持

  1. 加入 (宗教c,评级w)
  2. 删除 (宗教c,评级w)
  3. 询问宗教c的所有评级之和 map<int, int>
  4. 询问宗教c的所有评级最大值 set<pair<int, int> >

https://www.luogu.com.cn/problem/P3979
P3979 遥远的国度
换根

https://www.luogu.com.cn/problem/P4315
P4315 月下“毛景树”
区间赋值
区间加法
询问最大值

https://www.luogu.com.cn/problem/P4092
P4092 [HEOI2016/TJOI2016]树
可以直接线段树,不用树链剖分

https://www.spoj.com/problems/GSS7/

https://www.spoj.com/problems/QTREE

https://www.spoj.com/problems/PT07J

https://www.spoj.com/problems/QTREE4

https://www.spoj.com/problems/QTREE5

https://www.luogu.com.cn/problem/P2056
P2056 [ZJOI2007]捉迷藏

https://csacademy.com/contest/round-64/task/find-the-tree/

https://github.com/ericliu859/AcmPaper/tree/master/树/路径问题/2009 - 漆子超《分治算法在树的路径问题中的应用》
2009 - 漆子超《分治算法在树的路径问题中的应用》

https://oi-wiki.org/graph/hld/

https://www.luogu.com.cn/problem/SP3978

P3384,P2590,P2486,P2146,P3178,P4092,P4315,P3979,P3313,SP375,SP1487,SP6779

ST

名称

ST的全称是Sparse Table,翻译成中文,怎么看都应该是ST,S表,稀疏表。
Range minimum query(RMQ)指区间最值问题。

区间最值

区间最值是一个非常经典的问题,解决的方法也多种多样。
对于需要在线的题目,往往使用线段树,或者ST解决。

算法原理

fi,jf_{i, j}存储ai,ai+1,,ai+2j1a_i, a_{i + 1}, \dots ,a_{i + 2^j - 1}的最值。如果询问[l,r][l, r]区间的话,计算出小于等于区间长度,最大22的次幂2b2^b,最终的答案就是max(fl,b,fr2b+1,b)\max(f_{l, b}, f_{r - 2^b + 1, b})
计算bb可以使用__builtin_clz或者是预处理以22为底的对数,不建议使用实数版的对数再取整。

预处理部分fi,0=aif_{i, 0} = a_ifi,j=min(fi,j1,fi+2j1,j1)f_{i, j} = \min(f_{i, j - 1}, f_{i + 2^{j - 1}, j - 1})

时间复杂度

预处理的时间复杂度O(nlogn)O(n \log n),回答单次询问的时间复杂度是O(1)O(1)

适用题目

ST和线段树相比,不支持修改操作,预处理时间更慢,但是单次询问时间更快。所以ST表比较适合询问操作显著大于数组长度的情况。

其他应用

只要同一个位置多次更新不影响答案,都是可以用ST的,比如最大公约数。

求解LCA的一个方法,是先求DFS序,LCA相当于区间最浅的点,可以转化为区间最值问题。
然后用RMQ解决,可以得到一个预处理O(nlogn)O(n \log n)单次询问O(1)O(1)的做法。
实际上这是+-1RMQ(即相邻的两项差值只有正负一)可以优化到预处理O(n)O(n),单词询问O(1)O(1)
不过这个优化在实际运行中效果不明显。

另一方面,求区间最值,可以用笛卡尔树,转化为LCA问题。
所以RMQ问题等价于LCA问题,并且两者的理论最优复杂度都是预处理O(n)O(n),单词询问O(1)O(1)

高维推广

类似树状数组,ST表很容易推广到高维的情况。即
fi,j,k,l=minix<i+2j,ky<j+2kax,yf_{i, j, k, l} = \min_{i \leq x < i + 2^j, k \leq y < j + 2^k} a_{x, y}
询问时需要将询问的矩形区域,用四个边长为22的次幂的矩形区域覆盖。

预处理部分,如果kkll都是00,那么直接用fi,j,0,0=ai,jf_{i, j, 0, 0} = a_{i, j}
否则可以选择某个方向,分成两个更小的矩形,求最值即可。

参考代码

一维情况,以Luogu P3865为例:

#include <bits/stdc++.h>
using namespace std;
int n, m;
int f[100020][18];
int lg[100020];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &f[i][0]);
	}
	for (int j = 1; 1 << j <= n; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		}
	}
	lg[1] = 0;
	for (int i = 2; i <= n; i++) {
		lg[i] = lg[i / 2] + 1;
	}
	for (int i = 0; i < m; i++) {
		int l, r;
		scanf("%d%d", &l, &r);
		int b = lg[r - l + 1];
		printf("%d\n", max(f[l][b], f[r - (1 << b) + 1][b]));
	}
	return 0;
}

参考题目

Luogu P3865
ST算法

poj 2019
二维ST

zoj 2859
二维ST

  1. Sparse Table / ST表
    空间 Space O(n log n)
    预处理时间 preprocess O(n log n)
    求最小值 query O(1)
    不支持修改 does not support change

f[i][j] 0 <= i < n && 0 <= j < log n

min(a[3], a[4], a[5], a[6], a[7], a[8])
min(min(a[3], a[4], a[5], a[6]), min(a[5], a[6], a[7], a[8]))

f[i][j]表示从i开始,长度为2**j的区间的最小值是多少
f[i][j] = min(a[i], a[i+1], ..., a[i+2**j-1])    from i, length 2**j
f[i][0] = a[i]
f[i][j] = f[i][j-1], f[i + (2 ** (j-1))][j-1]

f[3][2] = min(a[3], a[4], a[5], a[6])
f[7][2] = min(a[7], a[8], a[9], a[10])
f[3][3] = min(f[3][2], f[7][2]) = min(a[3], a[4] .. a[10])

min(a[3], a[4], .., a[8]) = min(min(a[3], a[4], a[5], a[6]), min(a[5], a[6], a[7], a[8])) = min(f[3][2], f[5][2])

min(a[3], a[4], .., a[10]) = min(f[3][3], f[3][3]) not min(f[3][2], f[7][2])

how to cover a interval of length 8
如果区间长度是8,怎么覆盖?
use 2 intervals of length 8 (even they are the same), not 2 intervals of length 4.
一定是用长度为8,而不是2个长度4的覆盖

避免长度为1的时候出错。

#include <bits/stdc++.h>
using namespace std;
int n, m;
int f[100020][18];
int lg[100020];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &f[i][0]);
	}
	for (int j = 1; 1 << j <= n; j++) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		}
	}
	lg[1] = 0;
	for (int i = 2; i <= n; i++) {
		lg[i] = lg[i / 2] + 1;
	}
	for (int i = 0; i < m; i++) {
		int l, r;
		scanf("%d%d", &l, &r);
		// lg 可以用 __builtin_clz
		// lg can be replaced with __builtin_clz;   count leading zeros
		int b = lg[r - l + 1];
		printf("%d ", min(f[l][b], f[r - (1 << b) + 1][b]));
	}
	return 0;
}


4
00000000 00000000 00000000 00000100
__builtin_clz(4) = 29
31 - __builtin_clz(4) = 2
31 - __builtin_clz(7) = 2

ST can be used to calculate GCD / LCM

gcd(a[1], a[2], a[3]) = gcd(gcd(a[1], a[2]), gcd(a[2], a[3]))

RMQ

区间最值

Sparse Table

线段树

笛卡尔树

理论最优

O(n) 预处理, O(1) 回答的做法

参考题目

参考资料

splay

treap

可并堆

简介

一般的堆支持

  1. 求最值
  2. 删除最值
  3. 插入任意值

假设再加入一个操作,合并两个堆,就需要用可并堆来实现。

左偏树

性质

  1. 节点的键值小于或等于它的左右子节点的键值。
  2. 节点的左子节点的距离不小于右子节点的距离。
  3. 节点的距离等于它的右子节点的距离加1。
  4. 一棵N个节点的左偏树root节点的距离最多为log(N+1)-1。

其他操作

增加一个节点:将原树和只有一个节点的树合并。
删除根节点:合并根节点的左右子树。

随机合并

左偏树记录的信息比较复杂,一个简单替代方案是随机合并,
这样因为树的期望高度不大,效率也很高。

启发式合并

每次把较小的集合中每个元素,依次删除,并加入到较大的集合中。

参考题目

Luogu P1552
注意到对于每个人,薪水总预算相同,所以可以用可并堆,如果超过预算删去最贵的人。

Luogu P3377
左偏树模板题。

Luogu P4331
论文题,也可以用堆做。

参考资料

https://blog.csdn.net/chenjiayi_yun/article/details/38347117

pbds

简介

内置的平衡树,支持询问第k大

SP1487 PT07J - Query on a tree III
子树第 k 大

P2146 [NOI2015]软件包管理器
树链剖分 DFS序列

P3178 [HAOI2015]树上操作
树链剖分 DFS序列

P2486 [SDOI2011]染色
树链剖分 维护颜色段数

SP6779 GSS7 - Can you answer these queries VII
树上维护最大子段和

P1967 货车运输
CF609E Minimum spanning tree for each edge
一个特殊的做法: 启发式合并 + 暴力
另一个特殊的做法: Kruskal合并时就回答两个集合询问的交集

P4180 [BJWC2010]严格次小生成树
上一题的基础上维护次小,就不能用投机取巧的方法了

P4374 [USACO18OPEN]Disruption P
离线,问子树内向外连边中权值最小是多少

P5423 [USACO19OPEN]Valleys P
并查集,维护有没有洞

P6572 [BalticOI 2017] Railway
虚树,树上差分

SP3197 TREECST - Tree Construction
删一条边,加一条边,最小化树的直径

P6277 [USACO20OPEN]Circus P
有趣的计数题

中心点:度数 >= 3的点

一个中心点,空2个的情况
(n - 2)! / (距离中心点的边数 <= 1的点的个数 - 2)!

一个中心点,空3个的情况
(n - 3)! / (距离中心点的边数 <= 2的点的个数 - 3)!

一个中心点,空k个的情况
(n - k)! / (距离中心点的边数 <= (k-1)的点的个数 - k)!

多个中心点的情况?

如果两个中心点之间的距离是d (之间有d条边)
那么当空位个数 >= d + 2的时候,两个中心点会合并

for (pair<int, int> j: b[n - 1 - i])
{
c[F(j.first)] += j.second;
}
for (int jj = 0; jj < b[n - 1 - i].size(); jj++)
{
pair<int, int> j: b[n - 1 - i][jj];
c[F(j.first)] += j.second;
}

map<string, double> g;

for (pair<string, double> i: g)
{
i.first // key, string
i.second // value, double
}

P2986 [USACO10MAR]Great Cow Gathering G
树的重心

P3629 [APIO2010]巡逻
树的直径

P4183 [USACO18JAN]Cow at Large P
P4186 [USACO18JAN]Cow at Large G
点分治

P3806 【模板】点分治1
点分治或者启发式合并

P6329 【模板】点分树 | 震波 题解
动态点分治

树上需要统计所有路径
2种思路

  1. 让每个路径在LCA的位置被统计到
  2. 点分治,让每个路径在重心的位置被统计到

set s[10020];
void dfs(int x, int y, int d)
{
// s[x] 表示子树x中所有节点的深度(从根节点开始算)的集合
s[x].insert(d);
for (pair<int, int> i: a[x])
{
if (i.first != y)
{
dfs(i.first, x);
if (s[x].size() < s[i.first].size())
{
s[x].swap(s[i.first]);
}
枚举所有询问
{
if (这个询问已经被回答了)
{
continue;
}
for (int j: s[i.first])
{
if (s[x].find(询问的长度 + 2 * d - j) != s[x].end())
{
找到了
}
}
}
for (int j: s[i.first])
{
s[x].insert(j);
}
}
}
}
可以在 n log n 的时间内回答一个询问

DFS序

DFS序即用DFS访问一棵树时,访问节点的顺序。
比如这棵树

1 2
1 3
2 4
2 5

不妨认为11号节点是根节点。

二叉树的DFS序

先序排列即先访问根节点,再访问左孩子,最后访问右孩子。
中序排列即先访问左孩子,再访问根节点,最后访问右孩子。
后序排列即先访问左孩子,再访问右孩子,最后访问根节点。

先序排列:1 2 4 5 3
中序排列:4 2 5 1 3
后序排列:4 5 2 3 1

已知中序排列,和先序排列,可以还原二叉树,并推出后序排列。
已知中序排列,和后序排列,可以还原二叉树,并推出先序排列。
但是已知先序排列和后序排列,可能无法唯一确定二叉树。

进出栈序

1 2 4 4 5 5 2 3 3 1

即进栈记录一次,出栈记录一次,每个点在序列中出现两次,序列总长度为2n2n

子树在序列中是连续的一段,比如2 4 4 5 5 2就是原树的一段
所以这种DFS序可以用来解决 子树/单点 修改,子树/单点 询问。
如果结合树链剖分,每到一个点,先DFS重链所在的子树,那么也可以支持链上的修改和询问。

欧拉序

1 2 4 2 5 2 1 3

即每访问一个点,记录一次,每个点在序列中出现度数次,序列总长度为2n12n-1
一般不记录最后一次回到根节点,以满足以上性质。(否则根节点出现次数是度数+1)

欧拉序最常见的用法就是和ST结合起来求LCA。
两个点(如果一个点出现多次,任意一个均可)之间深度最浅的点,即为这两个点的LCA。

参考题目

Luogu P1030
给出一棵二叉树的中序与后序排列。求出它的先序排列。

Luogu P2146
维护一棵树,支持子树和链的操作。
树链剖分,然后求DFS序,先访问重链,这样重链在DFS序中是连续的。

字符串

字符串读入输出

Hash

KMP算法

hdu 4468 Spy
一个 KMP 好题

AC自动机

后缀自动机

AC自动机 (Aho–Corasick algorithm)

如何建树

AC自动机可以被补全成Trie图。

好处是不用每次跳Fail指针

\href{https://wenku.baidu.com/view/9df73e3567ec102de2bd89ae.html}{论文一}

\href{https://wenku.baidu.com/view/e0cc22d3240c844769eaeeac.html}{论文二}

\href{https://blog.csdn.net/thy_asdf/article/details/47082915}{正确的AC自动机写法。}

Fail Tree

自己 孩子节点的 Fail 指针 = 自己Fail指针节点的孩子

AC自动机 / TRIE图

扩展KMP的应用
AC自动机

参考题目

P3796 【模板】AC自动机(加强版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[200020][26], tt;
int f[200020];
int s[200020];
int a[200020];
int q[200020], qq;
char c[200][80];
char o[1000080];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	while (true)
	{
		scanf("%d", &n);
		if (n == 0)
		{
			break;
		}
		tt = 0;
		memset(t, 0, sizeof t);
		memset(f, 0, sizeof f);
		memset(s, 0, sizeof s);
		for (int i = 0; i < n; i++)
		{
			scanf("%s", c[i]);
			a[i] = ins(c[i]);
		}
		qq = 0;
		q[qq++] = 0;
		for (int j = 0; j < qq; j++)
		{
			int u = q[j];
			for (int i = 0; i < 26; i++)
			{
				if (t[u][i])
				{
					f[t[u][i]] = u ? t[f[u]][i] : 0;
					q[qq++] = t[u][i];
				}
				else
				{
					t[u][i] = t[f[u]][i];
				}
			}
		}
		scanf("%s", o);
		for (int p = 0, i = 0; o[i]; i++)
		{
			p = t[p][o[i] - 'a'];
			s[p]++;
		}
		for (int i = tt; i > 0; i--)
		{
			s[f[q[i]]] += s[q[i]];
		}
		int mx = 0, cnt = 0;
		for (int i = 0; i < n; i++)
		{
			if (mx < s[a[i]])
			{
				mx = s[a[i]];
				cnt = 1;
			}
			else if (mx == s[a[i]])
			{
				cnt++;
			}
		}
		printf("%d\n", mx);
		for (int i = 0; i < n; i++)
		{
			if (mx == s[a[i]])
			{
				printf("%s\n", c[i]);
			}
		}
	}
	return 0;
}

P3808 【模板】AC自动机(简单版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[1000020][26], tt;
int f[1000020];
int s[1000020];
int a[1000020];
int q[1000020], qq;
char c[2000020];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%s", c);
		a[i] = ins(c);
	}
	q[qq++] = 0;
	for (int j = 0; j < qq; j++)
	{
		int u = q[j];
		for (int i = 0; i < 26; i++)
		{
			if (t[u][i])
			{
				f[t[u][i]] = u ? t[f[u]][i] : 0; // 孩子的fail 指针 = fail 指针的孩子,根节点特殊处理
				q[qq++] = t[u][i];
			}
			else
			{
				t[u][i] = t[f[u]][i];
			}
		}
	}
	scanf("%s", c);
	for (int p = 0, i = 0; c[i]; i++)
	{
		p = t[p][c[i] - 'a'];
		s[p]++;
	}
	for (int i = tt; i > 0; i--)
	{
		s[f[q[i]]] += s[q[i]];
	}
	int z = 0;
	for (int i = 0; i < n; i++)
	{
		z += s[a[i]] > 0;
	}
	printf("%d\n", z);
	return 0;
}

P5357 【模板】AC自动机(二次加强版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[200020][26], tt;
int f[200020];
int s[200020];
int a[200020];
int q[200020], qq;
char c[2000020];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%s", c);
		a[i] = ins(c);
	}
	q[qq++] = 0;
	for (int j = 0; j < qq; j++)
	{
		int u = q[j];
		for (int i = 0; i < 26; i++)
		{
			if (t[u][i])
			{
				f[t[u][i]] = u ? t[f[u]][i] : 0;
				q[qq++] = t[u][i];
			}
			else
			{
				t[u][i] = t[f[u]][i];
			}
		}
	}
	scanf("%s", c);
	for (int p = 0, i = 0; c[i]; i++)
	{
		p = t[p][c[i] - 'a'];
		s[p]++;
	}
	for (int i = tt; i > 0; i--)
	{
		s[f[q[i]]] += s[q[i]];
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d\n", s[a[i]]);
	}
	return 0;
}

P4052 [JSOI2007]文本生成器

P3041 [USACO12JAN]Video Game G

P5840 [COCI2015]Divljak

P3311 [SDOI2014] 数数

P2536 [AHOI2005]病毒检测

P3121 [USACO15FEB]Censoring G

P2414 [NOI2011] 阿狸的打字机

P3966 [TJOI2013]单词

P5231 [JSOI2012]玄武密码

P3167 [CQOI2014]通配符匹配

P2336 [SCOI2012]喵星球上的点名

P2603 [ZJOI2008]无序运动

P3715 [BJOI2017]魔法咒语

P2292 [HNOI2004]L语言

P4045 [JSOI2009]密码

KMP

实现方法有很多,简单介绍一种

ababababab
0012345678

abababa
abababa
ababa
ababa
aba
aba
a
a

ababababcdddddd
ababababab
0012345678
ababababab

abacabadabacaba
001012301234567

abacabadabacabad
0010123012345678

abacabadabacabac
0010123012345674

abacabadabacabab
0010123012345672

abacabadabacabaa
0010123012345671

abacabadabacabae
0010123012345670

对于每个前缀 s[1..i]
找到最长的前缀等于后缀的长度p[i](不能是整个前缀
p[i] < i
s[1..p[i]] == s[i-p[i]+1..i]
长度为p[i]的前缀 以i结尾长度为p[i]的后缀
最长的是p[i]
如果希望找到所有合法的解
p[i]
p[p[i]]
p[p[p[i]]]
....
直到0

一些人的定义/实现,在求p[i]时,不仅要求
s[1..p[i]] == s[i-p[i]+1..i]
还要求s[p[i]+1] != s[i]
这是没有必要的,我们不采用这种写法

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa
aaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaab
aaaaaaaaab
aaaaaaaaab

abcabcabcabcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc
abcabcabc

参考代码

#include <bits/stdc++.h>
using namespace std;
char s[1000020];
char t[1000020];
int p[1000020];
int main() {
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    int n = strlen(s + 1);
    int m = strlen(t + 1);
    int u = 0;
    for (int i = 2; i <= m; i++) {
        while (u > 0 && t[u + 1] != t[i]) {
            u = p[u];
        }
        if (t[u + 1] == t[i]) {
            u++;
        }
        p[i] = u;
    }
    u = 0;
    for (int i = 1; i <= n; i++) {
        while (u > 0 && t[u + 1] != s[i]) {
            u = p[u];
        }
        if (t[u + 1] == s[i]) {
            u++;
        }
        if (u == m) {
            printf("%d\n", i - m + 1);
            u = p[u]; // 找到的匹配允许有重叠
            // u = 0; // 找到的匹配不允许有重叠
        }
    }
    for (int i = 1; i <= m; i++) {
        printf("%d%c", p[i], i == m ? '\n' : ' ');
    }
}

P4391

cabcabca
00012345
bca
bca
ca

P3435

寻找最长的周期
不能是整个串本身

babababa 6
bababa

bababab 6
bababa

bababa 4
baba

babab 4
baba

baba 2
ba

bab 2
ba

ba 0
ba

b 0

#include <bits/stdc++.h>
using namespace std;
char s[1000020];
int p[1000020];
int f[1000020];
int n;
long long z;
int main() {
	scanf("%d", &n);
	scanf("%s", s + 1);
	int u = 0;
	f[1] = 1;
	for (int i = 2; i <= n; i++) {
		while (u > 0 && s[u + 1] != s[i]) {
			u = p[u];
		}
		if (s[u + 1] == s[i]) {
			u++;
		}
		p[i] = u;
		f[i] = i;
		if (p[i] > 0)
		{
			f[i] = f[p[i]];
		}
		z += i - f[i];
	}
	printf("%lld\n", z);
	return 0;
}

对于每个位置,按照p[i]往0跳,f[i]是最后一步是从多少跳到0的

hdu 1711 Number Sequence

http://acm.hdu.edu.cn/showproblem.php?pid=1711
输入两个数字串匹配,找到第一次匹配的位置。
将KMP算法由字符串,推广到一般的数字序列。

hdu 2087 剪花布条

http://acm.hdu.edu.cn/showproblem.php?pid=2087

输入两个字母串匹配,问第二个在第一个中出现多少次。
字符串匹配,求匹配到的子串个数,不能重叠,问最多匹配多少个。
比如aaaaaaaaa中包含3个aaa,而不是7个。

hdu 2594 Simpsons’ Hidden Talents

http://acm.hdu.edu.cn/showproblem.php?pid=2594

找到最长的一个字符串,既是第一个串的前缀,又是第二个串的后缀。
连接起来,求next数组。

poj 3461 Oulipo

http://poj.org/problem?id=3461

字符串匹配,求匹配到的子串个数,可以重叠

poj 2406 Power Strings

http://poj.org/problem?id=2406

输入一个字符串求最小循环节,实际输出最小循环节的次数。
对于字符串计算next数组,考虑n-next[n]是否是n的约数。

arc060_d

https://beta.atcoder.jp/contests/arc060/tasks/arc060_d

输入一个字符串,分成尽量少的段,使得每段没有循环节。
输出最小段数,和方案数。

只有三种情况

后缀数组

height 数组

SA

https://www.luogu.com.cn/problem/solution/P5028
P5028 Annihilate

https://www.luogu.com.cn/problem/P2336
P2336 [SCOI2012]喵星球上的点名

什么是自动机?

link tree 的拓扑序?
建图 DFS,或者 按len计数排序

https://www.luogu.com.cn/problem/P3804
P3804 【模板】后缀自动机 (SAM)

https://www.luogu.com.cn/problem/P4070
P4070 [SDOI2016]生成魔咒

https://www.luogu.com.cn/problem/P4248
P4248 [AHOI2013]差异
正反一样

https://www.luogu.com.cn/problem/P4081
P4081 [USACO17DEC]Standing Out from the Herd P

https://www.luogu.com.cn/problem/P4022
P4022 [CTSC2012]熟悉的文章
后缀自动机,二分

https://www.luogu.com.cn/problem/solution/P3346
P3346 [ZJOI2015]诸神眷顾的幻想乡

https://www.luogu.com.cn/problem/P6793
P6793 [SNOI2020] 字符串

https://www.luogu.com.cn/problem/P4112
P4112 [HEOI2015]最短不公共子串

https://www.luogu.com.cn/problem/CF700E
CF700E Cool Slogans

https://www.luogu.com.cn/problem/SP8093
SP8093 JZPGYZ - Sevenk Love Oimaster

https://www.luogu.com.cn/problem/SP1811
SP1811 LCS - Longest Common Substring

https://www.luogu.com.cn/problem/SP1812
SP1812 LCS2 - Longest Common Substring II

https://www.luogu.com.cn/problem/solution/P1368
P1368 【模板】最小表示法

https://www.luogu.com.cn/problem/P2408
P2408 不同子串个数

https://www.luogu.com.cn/problem/P3181
P3181 [HAOI2016]找相同字符

【模板】后缀自动机 (SAM)
【模板】后缀排序
[JSOI2007]字符加密
DISUBSTR - Distinct Substrings
SUBST1 - New Distinct Substrings
NSUBSTR - Substrings
[TJOI2017]DNA
[SDOI2016]生成魔咒
LCS - Longest Common Substring
LCS2 - Longest Common Substring II
SUBLEX - Lexicographical Substring Search
LONGCS - Longest Common Substring
[TJOI2015]弦论
[AHOI2013]差异
[HAOI2016]找相同字符
[NOI2016] 优秀的拆分
[NOI2015] 品酒大会
[USACO17DEC]Standing Out from the Herd P
[ZJOI2015]诸神眷顾的幻想乡
JZPGYZ - Sevenk Love Oimaster
[NOI2018] 你的名字
[SDOI2008]Sandy的卡片
[CTSC2012]熟悉的文章
[USACO06DEC]Milk Patterns G
SubString
[POI2000]公共串
Security
Cyclical Quest
Martian Strings
Little Elephant and Strings
Tricky and Clever Password
Fake News (hard)
Forbidden Indices
NSUBSTR2 - Substrings II
ADASTRNG - Ada and Substring
Editor
Stammering Aliens
Life Forms
DNA Sequencing
Anti-Rhyme Pairs

后缀数组
后缀自动机

P1117 [NOI2016] 优秀的拆分
P3804 【模板】后缀自动机 (SAM)
P4070 [SDOI2016]生成魔咒
P4081 [USACO17DEC]Standing Out from the Herd P
P4112 [HEOI2015]最短不公共子串
P4094 [HEOI2016/TJOI2016]字符串
P4770 [NOI2018]你的名字
P4248 [AHOI2013]差异
P4341 [BJWC2010]外星联络
P6095 [JSOI2015]串分割
P6793 [SNOI2020]字符串
P2178 [NOI2015]品酒大会
P2336 [SCOI2012]喵星球上的点名
P2337 [SCOI2012]喵星人的入侵
P2463 [SDOI2008]Sandy的卡片
P3763 [TJOI2017]DNA
P3649 [APIO2014]回文串
P3181 [HAOI2016]找相同字符
的应用

https://www.luogu.com.cn/problem/P3649
P3649 [APIO2014]回文串

https://www.luogu.com.cn/problem/P5685
P5685 [JSOI2013]快乐的 JYY

AC自动机 (Aho–Corasick algorithm)

如何建树

AC自动机可以被补全成Trie图。

好处是不用每次跳Fail指针

\href{https://wenku.baidu.com/view/9df73e3567ec102de2bd89ae.html}{论文一}

\href{https://wenku.baidu.com/view/e0cc22d3240c844769eaeeac.html}{论文二}

\href{https://blog.csdn.net/thy_asdf/article/details/47082915}{正确的AC自动机写法。}

Fail Tree

自己 孩子节点的 Fail 指针 = 自己Fail指针节点的孩子

AC自动机 / TRIE图

扩展KMP的应用
AC自动机

参考题目

P3796 【模板】AC自动机(加强版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[200020][26], tt;
int f[200020];
int s[200020];
int a[200020];
int q[200020], qq;
char c[200][80];
char o[1000080];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	while (true)
	{
		scanf("%d", &n);
		if (n == 0)
		{
			break;
		}
		tt = 0;
		memset(t, 0, sizeof t);
		memset(f, 0, sizeof f);
		memset(s, 0, sizeof s);
		for (int i = 0; i < n; i++)
		{
			scanf("%s", c[i]);
			a[i] = ins(c[i]);
		}
		qq = 0;
		q[qq++] = 0;
		for (int j = 0; j < qq; j++)
		{
			int u = q[j];
			for (int i = 0; i < 26; i++)
			{
				if (t[u][i])
				{
					f[t[u][i]] = u ? t[f[u]][i] : 0;
					q[qq++] = t[u][i];
				}
				else
				{
					t[u][i] = t[f[u]][i];
				}
			}
		}
		scanf("%s", o);
		for (int p = 0, i = 0; o[i]; i++)
		{
			p = t[p][o[i] - 'a'];
			s[p]++;
		}
		for (int i = tt; i > 0; i--)
		{
			s[f[q[i]]] += s[q[i]];
		}
		int mx = 0, cnt = 0;
		for (int i = 0; i < n; i++)
		{
			if (mx < s[a[i]])
			{
				mx = s[a[i]];
				cnt = 1;
			}
			else if (mx == s[a[i]])
			{
				cnt++;
			}
		}
		printf("%d\n", mx);
		for (int i = 0; i < n; i++)
		{
			if (mx == s[a[i]])
			{
				printf("%s\n", c[i]);
			}
		}
	}
	return 0;
}

P3808 【模板】AC自动机(简单版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[1000020][26], tt;
int f[1000020];
int s[1000020];
int a[1000020];
int q[1000020], qq;
char c[2000020];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%s", c);
		a[i] = ins(c);
	}
	q[qq++] = 0;
	for (int j = 0; j < qq; j++)
	{
		int u = q[j];
		for (int i = 0; i < 26; i++)
		{
			if (t[u][i])
			{
				f[t[u][i]] = u ? t[f[u]][i] : 0; // 孩子的fail 指针 = fail 指针的孩子,根节点特殊处理
				q[qq++] = t[u][i];
			}
			else
			{
				t[u][i] = t[f[u]][i];
			}
		}
	}
	scanf("%s", c);
	for (int p = 0, i = 0; c[i]; i++)
	{
		p = t[p][c[i] - 'a'];
		s[p]++;
	}
	for (int i = tt; i > 0; i--)
	{
		s[f[q[i]]] += s[q[i]];
	}
	int z = 0;
	for (int i = 0; i < n; i++)
	{
		z += s[a[i]] > 0;
	}
	printf("%d\n", z);
	return 0;
}

P5357 【模板】AC自动机(二次加强版)

#include <bits/stdc++.h>
using namespace std;
int n;
int t[200020][26], tt;
int f[200020];
int s[200020];
int a[200020];
int q[200020], qq;
char c[2000020];
int ins(char *s)
{
	int p = 0;
	for (; *s; s++)
	{
		if (t[p][*s - 'a'] == 0)
		{
			t[p][*s - 'a'] = ++tt;
		}
		p = t[p][*s - 'a'];
	}
	return p;
}
int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%s", c);
		a[i] = ins(c);
	}
	q[qq++] = 0;
	for (int j = 0; j < qq; j++)
	{
		int u = q[j];
		for (int i = 0; i < 26; i++)
		{
			if (t[u][i])
			{
				f[t[u][i]] = u ? t[f[u]][i] : 0;
				q[qq++] = t[u][i];
			}
			else
			{
				t[u][i] = t[f[u]][i];
			}
		}
	}
	scanf("%s", c);
	for (int p = 0, i = 0; c[i]; i++)
	{
		p = t[p][c[i] - 'a'];
		s[p]++;
	}
	for (int i = tt; i > 0; i--)
	{
		s[f[q[i]]] += s[q[i]];
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d\n", s[a[i]]);
	}
	return 0;
}

P4052 [JSOI2007]文本生成器

P3041 [USACO12JAN]Video Game G

P5840 [COCI2015]Divljak

P3311 [SDOI2014] 数数

P2536 [AHOI2005]病毒检测

P3121 [USACO15FEB]Censoring G

P2414 [NOI2011] 阿狸的打字机

P3966 [TJOI2013]单词

P5231 [JSOI2012]玄武密码

P3167 [CQOI2014]通配符匹配

P2336 [SCOI2012]喵星球上的点名

P2603 [ZJOI2008]无序运动

P3715 [BJOI2017]魔法咒语

P2292 [HNOI2004]L语言

P4045 [JSOI2009]密码

自动机

子序列自动机

AC自动机

后缀自动机

agc022_e

[MUMETOC]

abc254_a Last Two Digits

https://atcoder.jp/contests/abc254/tasks/abc254_b
输入一个3位数,输出最后两位(十位和个位)

abc254_b Practical Computing

https://atcoder.jp/contests/abc254/tasks/abc254_b
输出杨辉三角

abc254_c K Swap

https://atcoder.jp/contests/abc254/tasks/abc254_c
输入一个数组,只有下标之差恰好是k的两个位置才能交换
可以交换任意多次
问能不能让数组变成升序

abc254_d

https://atcoder.jp/contests/abc254/tasks/abc254_d
输入n,问有多少对正整数(i,j),i和j都小于等于n,并且i*j是完全平方数

abc254_e

https://atcoder.jp/contests/abc254/tasks/abc254_e
输入一个图,每个点度数至多是3
Q个询问,每次问到点x的距离小于等于k的点,所有点的标号之和
k至多是3

abc254_f

https://atcoder.jp/contests/abc254/tasks/abc254_f
一个n行m列的数字,第行第j列是a[i]+b[j]
每次问一个矩形区域的GCD

abc253_a Median?

https://atcoder.jp/contests/abc253/tasks/abc253_a
输入3个数字a, b, c判断b是不是中位数

abc253_b Distance Between Tokens

https://atcoder.jp/contests/abc253/tasks/abc253_b
输入字符矩阵,输出2个o之间的距离

abc253_c Max - Min Query

https://atcoder.jp/contests/abc253/tasks/abc253_c
维护 multiset
支持加入一个值
删去一个值x至多c次
输出最大值减去最小值的差

abc253_d FizzBuzz Sum Hard

https://atcoder.jp/contests/abc253/tasks/abc253_d
输入n和a和b
问1到n之间所有既不是a的倍数,也不是b的倍数的数字之和是多少?

abc253_e Distance Sequence

https://atcoder.jp/contests/abc253/tasks/abc253_e
输入n, m, k
问有多少个长度为n的数列
每一项在1到m之间
相邻两项差的绝对值大于等于k

abc253_f Operations on a Matrix

https://atcoder.jp/contests/abc253/tasks/abc253_f
维护一个n行m列的矩阵,初始都是0,支持三种操作
第l到r列加上x
第i行加上x
询问第i行第j列的值

abc253_g Swap Many Times

https://atcoder.jp/contests/abc253/tasks/abc253_g
输入一个n,把所有1<=i<=j<=n(i,j)列出来,排序
选择第L对到第R对
对于每对(x,y)交换a[x]a[y]
问最后排列是什么?

abc252_a

https://atcoder.jp/contests/abc252/tasks/abc252_a
输入一个数字
输出对应的ASCII字符

abc252_b

https://atcoder.jp/contests/abc252/tasks/abc252_a
输入N和K
输入长度为N的数组A
输入长度为K的数组B
问是否存在一个B[i]使得A[B[i]] 是 A数组中的最大值?
如果有,输出Yes,如果没有,输出No

abc252_c

https://atcoder.jp/contests/abc252/tasks/abc252_c
输入n个 0到9 10个数字构成的排列
如果在第i秒按下停止按钮,那么他会显示第i%10个字符(0秒最左的字符,9秒最右的字符)
同一秒只能按下一个停止按钮
目标是让这n个排列显示相同的字母
问最优情况下需要几秒

abc252_d

https://atcoder.jp/contests/abc252/tasks/abc252_d
输入一个长度为N的数组,从中选三个下标 i < j < k,且 a[i] a[j] a[k] 互不相同
问有多少种选法

abc252_e

https://atcoder.jp/contests/abc252/tasks/abc252_e
输入一个图,起点是1,图是无向边
问哪些边在最短路树上

abc252_f

https://atcoder.jp/contests/abc252/tasks/abc252_f
https://www.luogu.com.cn/problem/P1334
初始有长度为L的木棒
需要砍成N段,我们需要的长度A[i]
最后可能有剩余
把长度为K的砍成X和K-X需要K的代价

abc252_g

https://atcoder.jp/contests/abc252/tasks/abc252_g
输入一个排列
问有多少个有根树(1是根)的前序遍历是输入的排列
如果一个点有多个孩子,那么必须从最小的孩子开始DFS

abc252_e

https://atcoder.jp/contests/abc252/tasks/abc252_e

abc251_a

https://atcoder.jp/contests/abc251/tasks/abc251_a

abc251_b

https://atcoder.jp/contests/abc252/tasks/abc251_b

abc251_c

https://atcoder.jp/contests/abc252/tasks/abc251_c

abc251_d

https://atcoder.jp/contests/abc252/tasks/abc251_d

abc251_e

https://atcoder.jp/contests/abc252/tasks/abc251_e

abc251_f

https://atcoder.jp/contests/abc252/tasks/abc251_f

abc250_a Adjacent Squares

https://atcoder.jp/contests/abc250/tasks/abc250_a

abc250_b Enlarged Checker Board

https://atcoder.jp/contests/abc250/tasks/abc250_b

abc250_c Adjacent Swaps

https://atcoder.jp/contests/abc250/tasks/abc250_c

abc250_d 250-like Number

https://atcoder.jp/contests/abc250/tasks/abc250_d

abc250_e Prefix Equality

https://atcoder.jp/contests/abc250/tasks/abc250_e

abc249_a Jogging

https://atcoder.jp/contests/abc249/tasks/abc249_a
Takahashi 以B米每秒的速度 走A秒,歇C秒,循环
Aoki 以E米每秒的速度 走D秒,歇F秒,循环
问X秒,之后,谁领先
位置一样的话,输出 Draw

abc249_b Perfect String

https://atcoder.jp/contests/abc249/tasks/abc249_b
输入一个由大小写字母组成的字符串
如果这个字符串满足以下三个条件,输出Yes,否则输出No
包含大写字母
包含小写字母
不包含重复字符

abc249_c Just K

https://atcoder.jp/contests/abc249/tasks/abc249_c
输入N个字符串,从中选一个子集,使得恰好出现K次的字母最多
保证一个字母在一个字符串中至多出现一次(所以出现K次的字母,意味着出现在K个字符串)
只需要输出 在选取最优的子集情况下 恰好出现K次的字母的个数

abc249_d Index Trio

https://atcoder.jp/contests/abc249/tasks/abc249_d
输入一个长度为N的数字,问有多少个有序对(i,j,k)满足
A[i]=A[j]*A[k]

for (int j = 1; j <= n; j++)
{
    for (int k = 1; j * k <= n; k++)
    {
        cnt++;
    }
}
cnt 约等于 n log n

abc249_e RLE

https://atcoder.jp/contests/abc249/tasks/abc249_e
有一种缩写字符串的方法,比如 aaabbcccc 可以写成 a3b2c4
问有多少个长度为n的字符串,缩写之后长度比n小,方案数模P输出

f[i][j] 表示压缩之前的长度i,压缩之后的长度是j的方案数
假设下一组字母重复k次,那么
f[i + k][j + 1 + k的位数] += f[i][j] * (25或26,如果是第一个字母,那么有26种选择,否则只有25种)
最后把
f[n][i] (i<n)
求和就可以了

abc248_a Lacked Number

https://atcoder.jp/contests/abc248/tasks/abc248_a
输入一个长度为9的字符串,0到9共十个字符其中出现了9个,输出没出现的字符

abc248_b Slimes

https://atcoder.jp/contests/abc248/tasks/abc248_b
输入A,B和K,问A乘以几次K,可以>=B

abc248_c Dice Sum

https://atcoder.jp/contests/abc248/tasks/abc248_c
输入N,M,K
N个数字,每个数字的范围是1到M,N个数字的总和<=K
问方案数有多少种?

abc248_d Range Count Query

https://atcoder.jp/contests/abc248/tasks/abc248_d
输入长度为N个数字,Q个询问
每次询问一个区间[L, R]内有多少个数字X出现

abc248_e K-colinear Line

https://atcoder.jp/contests/abc248/tasks/abc248_e
输入N个点的坐标和一个整数K,问有多少条直线通过其中恰好K个点,如果无穷多个输出Infinity

abc248_f Keep Connect

https://atcoder.jp/contests/abc248/tasks/abc248_f
输入N,一个2行N列的网格,有3N-2条边
问删掉其中i(1<=i<=N-1)条边,保持图联通,有多少个方案?
方案数模质数P输出

abc247_a Move Right

https://atcoder.jp/contests/abc247/tasks/abc247_a
输入一个长度为4的01串,右移一位,移出界的就没了,最左边补0

abc247_b Unique Nicknames

https://atcoder.jp/contests/abc247/tasks/abc247_b
输入n个人的姓氏和名字,每个人要给自己取一个昵称
昵称必须是自己的姓氏或名字
昵称不能是别人的姓氏,不能是别人的名字
问能不能每个人都取一个符合要求的昵称

abc247_c 1 2 1 3 1 2 1

https://atcoder.jp/contests/abc247/tasks/abc247_c

S[1] = [1]
S[2] = S[1] + [2] + S[1]
...
S[i] = S[i-1] + [i] + S[i-1]

输入n,输出S[n]

abc247_d Cylinder

https://atcoder.jp/contests/abc247/tasks/abc247_d
维护一个队列,支持两个操作
1 x c在队尾加入cx
2 c队首出队c个数字,输出所有出队的数字之和

abc247_e Max Min

https://atcoder.jp/contests/abc247/tasks/abc247_e
输入一个长度为n个数字,和最大值X,和最小是Y
问数组中有几个区间最大值是X且最小值是Y

所有数字分为>Y==Y<X==XX<..<Y五种情况
>Y<X的位置一定不能选
选一个区间,包含至少一个==Y,包含至少一个==X,问有多少种方案
问以每个位置作为结尾,有多少个方案
对于每个位置,找自己之前的最近的X是多少,最近的Y是多少,至多选多少个数字没有>Y<X的情况

abc247_f Cards

https://atcoder.jp/contests/abc247/tasks/abc247_f
有n张牌,第i张牌正面是Pi,反面是Qi。保证Pi和Qi是1到n的排列
从所有的牌中选一个子集,使得从1到n的每个数字,都出现过(正面或反面)

把每张牌看做是连接Pi和Qi的一条边
因为Pi和Qi都是排列,所以整个图一定是由若干环组成的
对于每个环来说,选取一个子集,不能有连续的两条边不选
这是一个经典问题,答案是Lucas Number,是以2 1 3 4 7 ...开始的Fibonacci数列
不同环之间不会互相影响,把不同环的答案乘起来就可以了

abc246_a Four Points

https://atcoder.jp/contests/abc246/tasks/abc246_a
输入矩形的四个顶点中的三个,问最后一个顶点是什么?

abc246_b Get Closer

https://atcoder.jp/contests/abc246/tasks/abc246_b
从点(0,0)向点(A,B)的方向走1的距离,问会到哪个点,输出小数
保证点(0,0)到点(A,B)之间的距离大于等于1

要找到两个数字(X,Y)
满足X*X+Y*Y=1X/Y=A/B

abc246_c Coupon

https://atcoder.jp/contests/abc246/tasks/abc246_c
买n个商品,第i个商品价格是ai,有k个优惠券,每个优惠券可以优惠X元
优惠券可以在同一个商品上叠加,但是价格不能为负数
问最优情况下买这n个商品需要多少钱?

abc246_d 2-variable Function

https://atcoder.jp/contests/abc246/tasks/abc246_d
输入N,找到一个数字X,使得X>=N且存在非负整数a和b使得X=a*a*a+a*a*b+a*b*b+b*b*b
问最小的X是多少?

abc246_e Bishop 2

https://atcoder.jp/contests/abc246/tasks/abc246_e
国际象棋棋盘,有障碍物,问象从起点到终点最少需要几步?
先输入N,表示行数和列数
然后输入起点的坐标Ax和Ay
然后输入终点的坐标Bx和By
然后输入N行,表示棋盘的初始状态,其中.表示空,#表示障碍物

abc246_f typewriter

https://atcoder.jp/contests/abc246/tasks/abc246_f
有一个打字机,打字机有N行,第i行有Si这些字母(不同行可能有相同的字母)
打字的过程是选一行(之后不能在变了)只能用这行的字母,生成一个长度为L的字符串
问一共有多少种可能字符串(可能选不同行能生成同一个字符串,只算一次)

abc245_a Good morning

https://atcoder.jp/contests/abc245/tasks/abc245_a
Takahashi于 A时B分0秒起床
Aoki于 C时D分1秒起床
问谁起的早

abc245_b Mex

https://atcoder.jp/contests/abc245/tasks/abc245_b
输入一个数组,问最小的,不在这个数组里的非负整数是什么?

abc245_c Choose Elements

https://atcoder.jp/contests/abc245/tasks/abc245_c
输入两个长度为N数组AB
问能否生成一个新的长度为N的数组X
X[i]A[i]或者B[i]
并且X中任意相邻两项的差<=K

abc245_d Polynomial division

https://atcoder.jp/contests/abc245/tasks/abc245_d
多项式除法
输入一个N次多项式A
输入一个N+M次多项式C
找到一个次数是M的多项式B
使得多项式A乘以多项式B等于多项式C
不需要取模,可能有负数,保证有解

abc245_e Wrapping Chocolate

https://atcoder.jp/contests/abc245/tasks/abc245_e
n个巧克力,第i个巧克力宽A[i]B[i]
m个盒子,第i个盒子宽C[i]D[i]
每个盒子至多装1个巧克力
如果A[i]<=C[j]&&B[i]<=D[j]那么第i个巧克力可以被装进第j个盒子中
问能不能把所有巧克力都装进盒子

abc245_f Endless Walk

https://atcoder.jp/contests/abc245/tasks/abc245_f
输入一个有向图,N个点M条边
问有多少个点,作为起点,可以找到无限长的路径?

abc244_a Last Letter

https://atcoder.jp/contests/abc244/tasks/abc244_a
输入字符串,输出最后一个字符

abc244_b Go Straight and Turn Right

https://atcoder.jp/contests/abc244/tasks/abc244_b
在平面上,初始x轴正方向,S字符表示向前一步,R字符表示右转90度
问最终坐标是什么

abc244_c Yamanote Line Game

https://atcoder.jp/contests/abc244/tasks/abc244_c
输入一个数字N,你和对方轮流说出一个1到2*N+1的数字
说过的数字不能再说
谁没有数字说,谁就输了
对方输入0,说明你赢了
fflush(stdout)

abc244_d Swap Hats

https://atcoder.jp/contests/abc244/tasks/abc244_d
输入两个RGB的排列
一次操作就是交换两个字符
问能不能操作恰好10**18
把第一个排列变成第二个排列

abc244_e King Bombee

https://atcoder.jp/contests/abc244/tasks/abc244_e
输入一个N个点,M个边的无向图
问有多少条路径,起点是S,终点是T,经过K条边
有一个点X特殊(X不等于S和T),要求必须出现偶数次,其他点没有要求

abc244_f Shortest Good Path

https://atcoder.jp/contests/abc244/tasks/abc244_f
输入一个图,n个点,m条边
2**n个询问
每个询问是一个长度为n的01串,问最短路径
如果s[i]==0那么要求第i个点必须在路径中出现偶数次
如果s[i]==1那么要求第i个点必须在路径中出现奇数次
问这2**n个询问的最短路径长度之和是多少?

abc243_a Shampoo

https://atcoder.jp/contests/abc243/tasks/abc243_a
初始有V毫升,三个人轮流取A,B,C毫升
问是谁先取不够自己想取的容量
如果是第一个人,输出F
如果是第二个人,输出M
如果是第三个人,输出T

abc243_b Hit and Blow

https://atcoder.jp/contests/abc243/tasks/abc243_b
输入两个长度相等的序列A和B,保证A中的数字互不相同,B中的数字互不相同
问有多少个i使得A[i]==B[i]
问有多少对i,j使得i!=j&&A[i]==B[j]

abc243_c Collision 2

https://atcoder.jp/contests/abc243/tasks/abc243_c
平面上有n个点,每个点的移动方向一定是左或右
问所有点按照自己的移动方向前进,是否会发生碰撞?

abc243_d Moves on Binary Tree

https://atcoder.jp/contests/abc243/tasks/abc243_d
一个二叉树,i节点的左孩子是2i,右孩子是2i+1
有三种操作,
U跳到当前点的父亲节点
L跳到当前点的左孩子节点
R跳到当前点的右孩子节点
输入起点X和跳的过程S问最终所在的点的标号是多少?
保证最终答案至多10**18但是中间可能超过10**18

abc243_e Edge Deletion

https://atcoder.jp/contests/abc243/tasks/abc243_e
输入一个n个点,m条边的连通,无向图
问至多可以删多少条边,使得任意两点间的最短距离不变

abc243_f Lottery

https://atcoder.jp/contests/abc243/tasks/abc243_f
抽奖,一共有N种不同的奖,第i种抽中的概率是Wi/所有W之和
问抽K次,恰好抽到了M个不同的奖,概率是多少?取模输出
M<=N<=50,K<=50

abc243_g Sqrt

https://atcoder.jp/contests/abc243/tasks/abc243_g
输入X,问有多少个
长度100
第一项是X
之后每一项的平方都小于等于前一项
的正整数数列
多组测试数据,不需要取模

abc242_a T-shirt

https://atcoder.jp/contests/abc242/tasks/abc242_a
第1名到第A名之间一定能拿到T-shirt
第A+1名到第B名之间随机抽C个T-shirt
一个人是第X的名,问拿到T-shirt概率是多少?

abc242_b Minimize Ordering

https://atcoder.jp/contests/abc242/tasks/abc242_b
输入一个字符串S,重排S中的字母,使得结果字典序最小

abc242_c 1111gal password

https://atcoder.jp/contests/abc242/tasks/abc242_c
输入N,问有多少个N位数字,每一位是1~9,并且相邻两位的差至多是1
答案模 998244353 输出

abc242_d ABC Transform

https://atcoder.jp/contests/abc242/tasks/abc242_d
输入一个只包含字母ABC的字符串S,一次操作会把所有的A变成BC,B变成CA,C变成AB
有Q个询问,第i个询问问,操作ti次之后,第si个字符是什么?

变化过程
A
BC
CAAB
ABBCBCCA

A->0 B->1 C->2

0
12
2334
34454556

0
01
0112
01121223
这个数列就是数字i(从0开始)的二进制中有几个1

所以 34454556 .. 的第t行的第k个是
(t + (k-1)的二进制中有几个1) % 3 + 初始的字符,就是答案

abc242_e (∀x∀)

https://atcoder.jp/contests/abc242/tasks/abc242_e
T组数据,每次数据输入一个字符串S
问有多少个字符串和S长度相同,且字典序小于等于S
答案模 998244353 输出

abc242_f Black and White Rooks

https://atcoder.jp/contests/abc242/tasks/abc242_f
n行m列的网格,放b个黑车,w个白车
要求放置的车不能互相攻击
(同一行不能同时有黑车和白车,同一列不能同时有黑车和白车)
答案模 998244353 输出

abc241_a Digit Machine

https://atcoder.jp/contests/abc241/tasks/abc241_a
输入一个长度为10的数组a[0], a[1], ..., a[9]
如果当前是x,那么按一下按钮会变成a[x]
初始是0,问按3次按钮之后会变成什么数字?

abc241_c Connect 6

https://atcoder.jp/contests/abc241/tasks/abc241_c
输入n,和n*n的棋盘,其中.表示空,#表示棋子
六子棋,问再下至多两个子,能不能连成连续6个?

abc241_d Sequence Query

https://atcoder.jp/contests/abc241/tasks/abc241_d
维护一个集合,支持三种操作
1 x插入x,可能有重复的x
2 x k询问小于等于x的数字中,第k大的
3 x k询问大于等于x的数字中,第k小的

k至多是5

abc241_e Putting Candies

https://atcoder.jp/contests/abc241/tasks/abc241_e
输入长度为N的数字A,下标从0开始
初始X=0
每次操作X+=A[X%n]
问K次操作之后X是多少?

找循环节,一旦X%n的余数出现过,说明出现了循环节

abc240_a Edge Checker

https://atcoder.jp/contests/abc240/tasks/abc240_a
1到10连成一个环,输入两个数字,问这两个数字之间有没有边

abc240_c Jumping Takahashi

https://atcoder.jp/contests/abc240/tasks/abc240_c
从位置0出发,第i步前进ai或bi,问能不能恰好走N步,到位置X

f[i][j] 表示 走i步能不能到j

abc240_d Strange Balls

https://atcoder.jp/contests/abc240/tasks/abc240_d
依次加入n个数字,第i次加入ai
连续k个数字k会消失
问加入每个数字之后还剩下多少个数字?

abc239_a Horizon

https://atcoder.jp/contests/abc239/tasks/abc239_a
输入X,输出sqrt(X*(12800000+X))

abc239_b Integer Division

https://atcoder.jp/contests/abc239/tasks/abc239_b
输入X,输出X除以10下取整的结果。-10**18<=x<=10**10,X可能是负数

abc239_c Knight Fork

https://atcoder.jp/contests/abc239/tasks/abc239_c
问象棋中马能不能两步从(x1,y1)跳到(x2,y2)

abc239_d Prime Sum Game

https://atcoder.jp/contests/abc239/tasks/abc239_d
甲从A到B中选一个数字
乙从C到D中选一个数字
如果两个数字的和是质数,那么乙获胜,否则甲获胜
问谁能赢?
如果甲获胜,输出Takahashi
如果乙获胜,输出Aoki

相当于问A到B中是否存在一个数字i,使得i+C到i+D之间都是合数
如果存在,那么甲获胜
如果不存在,那么乙获胜

abc238_a Exponential or Quadratic

https://atcoder.jp/contests/abc238/tasks/abc238_a
输入n,问是否2**n>n**2,输出YesNo

abc238_b Pizza

https://atcoder.jp/contests/abc238/tasks/abc238_b
一个Pizza切N+1刀
第0刀在0的位置上,之后切第i刀之前,先顺时针旋转Pizza Ai度,再下刀
问最后最大的一块,圆心角是多少?

abc238_c digitnum

https://atcoder.jp/contests/abc238/tasks/abc238_c
f(x)是小于等于x,且位数和x的位数相同的数字个数
输入n,求(f(1)+f(2)+...+f(n))%998244353

f(x)=x+1-小于等于x最大的10的次幂

abc238_d AND and SUM

https://atcoder.jp/contests/abc238/tasks/abc238_d
输入非负整数as,问是否存(x,y)使得(x&y)==a(x+y)==s
&是按位与

abc238_e Range Sums

https://atcoder.jp/contests/abc238/tasks/abc238_e
有一个长度为n的数组,已知其中q个区间的和,问能不能推断出所有数字的和?

abc238_f Two Exams

https://atcoder.jp/contests/abc238/tasks/abc238_f
n个人考两次试,每个人有两个排名(没有并列)
从中选去k个人,要求不存在 某个没被选上的人 两场考试都比 某个被选上的人 好
问方案数

一共n个人,选m个人
在第1场考试中的前i个人中,选了j个人,没选的人的人中第2场考试名次最好的是k

初始值
f[0][0][n+1]=1

要决定是否选择第一场考试的第i+1名
不选的话
f[i][j][k] -> f[i+1][j][min(k, 这个人的第2场考试名次)]
选的话,第一场考试的i+1名,在第二场中的名次必须<k
f[i][j][k] -> f[i+1][j+1][k]
(如果>k,就意味着存在一个人,第一场出现在i+1之前,且第2场考的还比i+1好)

最终答案 f[n][m][?] 求和

abc237_a Not Overflow

https://atcoder.jp/contests/abc237/tasks/abc237_a
输入一个 long long 范围(-2**63 <= N <= 2**63-1)内的整数 N
N 在不在 int 范围内(-2**31 <= N <= 2**31-1)
输出 YesNo

abc237_b Matrix Transposition

https://atcoder.jp/contests/abc237/tasks/abc237_b
输入一个H行,W列的矩阵,输出他的转置
注意数据范围,不能开一个100000*100000的数组
保证输入的 行数乘以列数 至多是100000
(行变成列,列变成行)

abc237_c kasaka

https://atcoder.jp/contests/abc237/tasks/abc237_c
输入一个字符串,问能不能在开始加若干个(可以是0个)a,使得字符串变成回文串

abc237_d LR insertion

https://atcoder.jp/contests/abc237/tasks/abc237_d
刚开始有一个数字是[0](只包含一个0
依次进行N个操作,
如果第i个操作是L,那么就把i插入到i-1的左边
如果第i个操作是R,那么就把i插入到i-1的右边

abc237_e Skiing

https://atcoder.jp/contests/abc237/tasks/abc237_e
有n个点,m条边,边是双向的,保证图是连通的,第i个点有高度hi
从点x到点y,如果是下降的,幸福值增加hx-hy
从点x到点y,如果是上升的,幸福值减少2*(hy-hx)
如果高度相同,那么幸福值不变
从点1出发,到任意一点结束,问幸福值最大是多少?

abc236_a chukodai

https://atcoder.jp/contests/abc236/tasks/abc236_a
输入一个字符串S,交换第a个和第b个字符,输出交换之后的结果

abc236_b Who is missing?

https://atcoder.jp/contests/abc236/tasks/abc236_b
从4个1,4个2……4个n中删去一个数字,输出剩下的4n-1个数字,问被删去的是什么?

abc236_c Route Map

https://atcoder.jp/contests/abc236/tasks/abc236_c
一个铁路上有n个站,输入n个站的名字
但是快车不停所有站,只停其中m个,输入这m个站的名字
对于每个站,问快车停不停?

abc236_d Dance

https://atcoder.jp/contests/abc236/tasks/abc236_d
输入2n个人,要组成n对,已知每两个人组队的亲和力
最终的欢乐值是所有n对亲和力的异或
问欢乐值最大是多少?

abc236_e Average and Median

https://atcoder.jp/contests/abc236/tasks/abc236_e
输入n个数字,从中选取若干个数字,要求任意连续两个数字,至少选了其中一个
问选取的数字,平均值最大是多少?中位数最大是多少?

abc236_f Spices

https://atcoder.jp/contests/abc236/tasks/abc236_f
2**n-1种香料,每种香料有一个价格,只要买了就可以无限的使用
如果有i香料和j香料,可以合成出i^j香料
希望买一些香料,可以合成出所有香料,问最少花多少钱?

abc235_a Rotate

https://atcoder.jp/contests/abc235/tasks/abc235_a
输入三位数abc
输出abc+bca+cab

abc235_b Climbing Takahashi

https://atcoder.jp/contests/abc235/tasks/abc235_b
输入一个数组,从左向右看,问第一次不变或减少是什么数字

abc235_c The Kth Time Query

https://atcoder.jp/contests/abc235/tasks/abc235_c
输入一个数组
询问x和k
问第k次出现x的位置

map<int, vector<int> > g;

abc235_d Multiply and Rotate

https://atcoder.jp/contests/abc235/tasks/abc235_d
输入a和N
从1操作到N
每次可以乘以a
或者是把个位移动到最高位 123 -> 321
问至少需要几步

abc235_e MST + 1

https://atcoder.jp/contests/abc235/tasks/abc235_e
先求MST题
每次询问一条边,问加入这条边能不能让MST变小 / 问这条边是不是这条路径上最大的

abc235_f Variety of Digits

https://atcoder.jp/contests/abc235/tasks/abc235_f
输入N,和M个1位数(没有重复)
问1到N中,包含所有M位的数字,和是多少
(前导0不算包含0)
状态压缩数位动态规划
f[i][j] i位数字包含j这些位数,的方案数,允许前导0

数位DP

abc235_g Gardens

https://atcoder.jp/contests/abc235/tasks/abc235_g
A个A种子
B个B种子
C个C种子
放到N个花园里
花园可区分
种子不可区分
每个花园至少一个种子
每个花园每种种子至多一个
种子不需要用完

反演

abc234_a Weird Function

https://atcoder.jp/contests/abc234/tasks/abc234_a
定义f(x)=x*x+2*x+3
输入t,输出f(f(f(t)+t)+f(f(t)))
保证结果在 int 范围内

abc234_b Longest Segment

https://atcoder.jp/contests/abc234/tasks/abc234_b
输入N个点,从中选两个点,选出的两个点之间距离最大是多少?

abc234_c Happy New Year!

https://atcoder.jp/contests/abc234/tasks/abc234_c
问第K小的,只包含0和2的正整数是什么?

abc234_d Prefix K-th Max

https://atcoder.jp/contests/abc234/tasks/abc234_d
输入一个排列,和一个数字K
问对于所有长度大于等于K的前缀,第K大数字是什么?

abc234_e Arithmetic Number

https://atcoder.jp/contests/abc234/tasks/abc234_e
输入一个数字X,问大于等于X,最小的等差数列数是多少?
等差数列数:单独看每一位,是个等差数列

abc234_f Reordering

https://atcoder.jp/contests/abc234/tasks/abc234_f
输入一个字符串,长度至多5000
从字符串中选一个子序列
然后再重排这个子序列,问有多少种可能的结果?

选子序列的时候,重要的是每种字符选了多少个,顺序不重要
所以输入的s不重要,重要的是s中每种字母多少个
f[i][j]前i种字母,总长度是j,方案有多少种

for i
for j
for k // 第i+1种字母选了多少个?
f[i + 1][j + k] += f[i][j] * c[j + k][k]

abc233_a 10yen Stamp

https://atcoder.jp/contests/abc233/tasks/abc233_a
输入X和Y,问在X的基础上,再加几个10,可以>=Y

abc233_b A Reverse

https://atcoder.jp/contests/abc233/tasks/abc233_b
输入L和R,和字符串S,翻转第L个字符到第R个字符所组成的区间,输出
字符串从1开始下标,包含第L个和第R个

abc233_c Product

https://atcoder.jp/contests/abc233/tasks/abc233_c
输入N个数组,和一个整数X,第i个数组长度是Li,内容是aij
每个数组里取一个数字,要求乘积等于X,问方案数

abc233_d Count Interval

https://atcoder.jp/contests/abc233/tasks/abc233_d
输入一个数组,有正有负,问有多少个区间和为K

abc233_e Σ[k=0..10^100]floor(X/10^k)

https://atcoder.jp/contests/abc233/tasks/abc233_e
输入一个数字X,问所有前缀和是多少?
比如输入 1225 应该输出 1225 + 122 + 12 + 1 = 1360

abc233_f Swap and Sort

https://atcoder.jp/contests/abc233/tasks/abc233_f
给一些交换,问能不能排序输入的数列

直接贪心就可以了

abc232_a QQ solver

https://atcoder.jp/contests/abc232/tasks/abc232_a
输入AxB 输出A*B

abc232_b Caesar Cipher

https://atcoder.jp/contests/abc232/tasks/abc232_b
输入字符串S和T,你可以shift其中一个字符串
shift:每个字母都变成下一个字母,z变成a
问能否让两个字符串相等

abc232_c Graph Isomorphism

https://atcoder.jp/contests/abc232/tasks/abc232_c
输入两个图,问重新编号之后能否全等
这个题n特别小,需要枚举全排列
next_permutation
输入两个图

cin >> x >> y;
a[x][y] = a[y][x] = 1;
do
{
    bool f = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (a[i][j] != b[p[i]][p[j]])
            {
                f = 0;
            }
        }
    }
    if (f == 1)
    {
        cout << "Yes" << endl;
        return 0;
    }
} while (next_permutation(p + 1, p + n + 1)); // p 是一个排列 
cout << "No" << endl;
return 0;

abc232_d Weak Takahashi

https://atcoder.jp/contests/abc232/tasks/abc232_d
输入一个H行W列的数组
从左上角出发,每次只能向右或向下,问最多能经过多少个格子

f[i][j] 能否到 (i, j)
for (int i = 0; i <n; i+)
{
    for (int j = 0; j < m; )
    {
        if (自己不是障碍物 && (上面能到 || 左边能到))
        {
            自己能到
            z = max(z, i + j + 1);
        }
    }
 }
 
 
for (int i = 0; i <n; i+)
{
    for (int j = 0; j < m; j++)
    {
        if (自己不是障碍物 && 自己能到)
        {
            如果自己有下面 && 右边不是障碍物 
                下面能到
            如果自己有右边 && 右边不是障碍物 
                右边能到 
            z = max(z, i + j + 1);
        }
    }
}

abc232_e Rook Path

https://atcoder.jp/contests/abc232/tasks/abc232_e
国际象棋棋盘
一个车
从x1 y1 走到x2 y2
走恰好k步,问方案数

最基本的动态规划:
f[k][i][j] 走到 (i, j) 恰好k步的方案数
for (k=0 to 步数)
    for i 每行
        for j 每列
            f[k][i][j] = 0
            for (x, y)(i, j) 在同一行或同一列,但不是 (i, j)
                f[k][i][j] += f[k-1][x][y]

(步数+1,行数,列数)
因为步数从0开始
注意到 f[k] 是 H * W 的数组,数组里只有4种值
(x1, y1)
(x1, 非y1)
(非x1, y1)
(非x1, 非y1)
实际上不需要维护 H * W 的数组,只需要维护这四个值就可以了

0 1 0
0 0 0

1 0 1
0 1 0

1 3 1
2 0 2

6 2 6
3 7 3

未优化:
f[3][2][1] = 3
f[3][2][3] = 3

优化后:
f[3][非x1 非y1] = 3

H 行 W 列
(x1,y1) = (x1,非y1) * (W-1) + (非x1,y1) * (H-1)
(x1,非y1) = (x1,非y1) * (W-2) + (x1,y1) + (非x1,非y1) * (H-1)
(非x1,y1) = (非x1,y1) * (H-2) + (x1,y1) + (非x1,非y1) * (W-1)
(非x1,非y1) = (非x1,非y1) * (H+W-4) + (非x1,y1) + (x1,非y1)

注意到 H>=2 W>=2 所以没什么特殊情况
最后根据 x2是否等于x1 y2是否等于y1 决定输出四个中的哪个

abc232_f Simple Operations on Sequence

https://atcoder.jp/contests/abc232/tasks/abc232_f
输入两个数组,A和B
对A进行操作,可以+1或者-1, 花费X的代价
可以交换两个相邻的元素,花费Y的代价
问最小代价是什么

核心是决定A的一个排列(代价:交换次数 * Y)
和B比较,代价是 对应的位置绝对值的差 * X
核心就是用一个数字表示一个集合

f[A中用了哪些数字]  匹配B中前几个数字
for (每个集合i)
    for (不在集合中的j)
        f[集合i 并 元素j] = min(f[集合i 并 元素j],
            f[i] + abs(a[j]-b[该第几个元素了])*Y + 交换次数*X)
cout << f[全集] <<endl;

abc232_h King's Tour

https://atcoder.jp/contests/abc232/tasks/abc232_h
国际象棋的王遍历棋盘,从(1,1)出发,到(a,b)结束
构造出方案

每次走一条边?最后2x2特殊处理?

abc231_a Water Pressure

https://atcoder.jp/contests/abc231/tasks/abc231_a
输入D,输出D/100。如果答案有小数部分,要输出小数部分。

abc231_b Election

https://atcoder.jp/contests/abc231/tasks/abc231_b
输入N个字符串,问哪个字符串出现的次数最多?

abc231_c Counting 2

https://atcoder.jp/contests/abc231/tasks/abc231_c
输入N个数字Ai,Q个询问Xj
对于每个询问,问有多少个Ai大于等于Xj

abc231_d Neighbors

https://atcoder.jp/contests/abc231/tasks/abc231_d
N个人排队,其中M对人想紧挨着,问有没有可能

不能有度数大于等于3的点,不能有环

abc231_e Minimal payments

https://atcoder.jp/contests/abc231/tasks/abc231_e
有N种面值的硬币,想支付X的钱数,允许找钱
保证硬币的面值之间互为倍数
问支付加找回至少需要几个硬币

abc230_a AtCoder Quiz 3

https://atcoder.jp/contests/abc230/tasks/abc230_a
如果N>=42,先N++
输入N,输出AGCXXX,其中XXX应被替换为N,如果N不足三位补零

abc230_b Triple Metre

https://atcoder.jp/contests/abc230/tasks/abc230_b
输入字符串S,问S是不是oxx重复无限次的一个子串

abc230_c X drawing

https://atcoder.jp/contests/abc230/tasks/abc230_c
问象能攻击到的位置

abc230_d Destroyer Takahashi

https://atcoder.jp/contests/abc230/tasks/abc230_d
输入N个区间,[Li, Ri]
给定D,每次操作你可以选一个数字x,删去[x,x+D-1]中的所有数字
问至少需要多少次操作,才能让每个区间内都有数字被删掉

abc230_e Fraction Floor Sum

https://atcoder.jp/contests/abc230/tasks/abc230_e
输入N,对于i从1到N,求N/i的和

除法分组,商相同的一起算

abc229_a First Grid

https://atcoder.jp/contests/abc229/tasks/abc229_a
输入一个由.#构成的2行2列的字符矩阵,问所有#之间是不是四连通的

abc229_b Hard Calculation

https://atcoder.jp/contests/abc229/tasks/abc229_b
输入A和B,问计算A+B时是否需要进位

abc229_c Cheese

https://atcoder.jp/contests/abc229/tasks/abc229_c
输入n种物质,W克的限制
第i种物质两个属性,美味值Ai每克和上限Bi克
问能达到的最大美味值是多少

abc228_a On and Off

https://atcoder.jp/contests/abc228/tasks/abc228_a
输入S,T,X。S点整到T点整开灯,问X点30分是否开灯

abc228_b Takahashi's Secret

https://atcoder.jp/contests/abc228/tasks/abc228_b
输入N和X,和长度为N的数组Ai
X知道了一个秘密
如果i知道了这个秘密,他会告诉Ai
问最后多少人知道了这个秘密

abc228_c Final Day

https://atcoder.jp/contests/abc228/tasks/abc228_c
N个学生,4天考试,每天的成绩是0到300的整数,已知前3天的成绩
你来给出第四天的成绩,问对于每个人是否可能在前K名当中
名次 = 比自己成绩高的人数+1

abc227_a Last Card

https://atcoder.jp/contests/abc227/tasks/abc227_a
K张牌,轮流发给N个人,从第A个人开始发,问谁拿到最后一张牌?

abc227_b KEYENCE building

https://atcoder.jp/contests/abc227/tasks/abc227_b
输入n个Si
问对于哪些Si,肯定不能表示为4*a*b+3*a+3*b的形式,其中ab是正整数
输出无解的Si的个数

abc227_c ABC conjecture

https://atcoder.jp/contests/abc227/tasks/abc227_c
输入n,问0<A<=B<=CA*B*C<=N有多少组整数解

abc227_d Project Planning

https://atcoder.jp/contests/abc227/tasks/abc227_d
n个部门,第i个部门有ai个人,做一个项目需要来自k个不同部门的各1个人
问至多可以做多少个项目?
需要二分

abc226_a Round decimals

https://atcoder.jp/contests/abc226/tasks/abc226_a
输入一个实数,输出四舍五入到整数的结果
int(x+0.5)

abc226_b Counting Arrays

https://atcoder.jp/contests/abc226/tasks/abc226_b
输入n个数组
每个数组输入长度和内容
问有多少个互不相同的数组
set<vector<int> > s;
vector之间可以互相比较,可以先排序再去重

vector<vector<int> > s;
cin >> n;
while (n--)
{
    cin >> m;
    vector<int> a;
    while (m--)
    {
        cin >> x;
        a.push_back(x);
    }
    s.push_back(a);
}
sort(s.begin(), s.end());
s.resize(unique(s.begin(), s.end()) - s.begin());
cout << s.size() << endl;

abc226_c Martial artist

https://atcoder.jp/contests/abc226/tasks/abc226_c
一共n个技能,学第i个技能需要花Ti的时间
想学第i个技能需要先学前置条件 Ki个技能(也就是Ai数组中的技能
问学第n个技能需要多久

做法是倒序考虑所有技能,第n个一定学,前置技能标记上要学
枚举到一个技能,如果不需要学就不学,如果需要学就浪费Ti的时间学
并把所有前置技能都标记上需要学

abc226_d Teleportation

https://atcoder.jp/contests/abc226/tasks/abc226_d
从(x,y)使用技能(a,b)可以移动到(x+a,y+b)上
a和b你来选
种类越少越好
问至少需要几个技能,才能实现从任意一点,到另一点的移动
(在一次移动中只能使用一种技能)

abc226_e Just one

https://atcoder.jp/contests/abc226/tasks/abc226_e
输入一个无向图,你来给边定方向,让图变成有向图,要求每个点出度恰好为一
(入度无所谓

确认每个连通块边数=点数
答案是2**连通块个数

abc225_a Distinct Strings

https://atcoder.jp/contests/abc225/tasks/abc225_a
输入一个长度为3的字符串,问将其中的字符重新排列后,能得到多少个不同的字符串

abc225_b Star or Not

https://atcoder.jp/contests/abc225/tasks/abc225_b
输入一棵树,问是不是星形,即所有点都和其中某个点直接连接

abc225_c Calendar Validator

https://atcoder.jp/contests/abc225/tasks/abc225_c
有一个10000000000行7列的矩阵A,第i行第j列是(i-1)*7+j
输入N行M列的矩阵B,问是不是A的子矩阵

abc225_d Play Train

https://atcoder.jp/contests/abc225/tasks/abc225_d
链表

abc224_a Tires

https://atcoder.jp/contests/abc224/tasks/abc224_a
输入一个字符串,如果以er结尾,输出er,如果以ist结尾,输出ist。

abc224_b Mongeness

https://atcoder.jp/contests/abc224/tasks/abc224_b
输入一个二维数组,问对于所有1 <= i1 < i2 <= H, 1 <= j1 < j2 <= W
是否满足A[i1][j1] + A[i2][j2] <= A[i2][j1] + A[i1][j2]
全部都满足输出Yes,存在不满足的输出No

abc224_c Triangle?

https://atcoder.jp/contests/abc224/tasks/abc224_c
输入n个点,问有多少种选法从中选3个,构成一个面积为正的三角形(不能三点共线)

abc223_a Exact Price

https://atcoder.jp/contests/abc223/tasks/abc223_a
输入N,如果N大于0并且是100的倍数,输出Yes,否则输出No

abc223_b String Shifting

https://atcoder.jp/contests/abc223/tasks/abc223_b
输入一个字符串,可以循环左移或者循环右移
循环左移:把最左边的字符移到最右边 abcdef -> bcdefa
循环右移:把最右边的字符移到最左边 abcdef -> fabcde
问能得到的字典序最大的字符串,和字典序最小的字符串是什么
s = s.substr(1) + s.substr(0, 1)

abc223_h Xor Query

https://atcoder.jp/contests/abc223/tasks/abc223_h

abc222_a Four Digits

https://atcoder.jp/contests/abc222/tasks/abc222_a
输入一个数字,如果不是4位数,在开头补零,补到四位输出

    printf("%04d\n", 123);
    cout << setfill('0') << setw(4) << 123 << endl;
    print('%04d' % 123)

abc222_b Failing Grade

https://atcoder.jp/contests/abc222/tasks/abc222_b
输入N个数字,问其中有多少个<P

abc222_g 222

https://atcoder.jp/contests/abc222/tasks/abc222_g
输入K,问最少连续几个2,是K的倍数

Baby Step Gaint Step

abc221_a Seismic magnitude scales

https://atcoder.jp/contests/abc221/tasks/abc221_a
输入A和B,输出32的A-B次方
输出 (1<<(5*(A-B)))32**(A-B)

abc221_b typo

https://atcoder.jp/contests/abc221/tasks/abc221_b
输入字符串S和T,问能不能通过至多一次交换相邻字符操作,使得S等于T?

abc221_h Count Multiset

https://atcoder.jp/contests/abc221/tasks/abc221_h
有限背包,计数

容斥

abc220_a Find Multiple

https://atcoder.jp/contests/abc220/tasks/abc220_a
输入A, B, C。输出一个A和B之间C的倍数,如果没有输出-1

abc220_b Base K

https://atcoder.jp/contests/abc220/tasks/abc220_b
输入两个K进制的数字,用十进制输出他们的乘积

abc219_a AtCoder Quiz 2

https://atcoder.jp/contests/abc219/tasks/abc219_a
输入X
如果X<40输出还差多少到40
如果X<70输出还差多少到70
如果X<90输出还差多少到90
如果X>=90输出expect

abc219_b Maritozzo

https://atcoder.jp/contests/abc219/tasks/abc219_b
输入三个字符串S1, S2, S3和一个只包含123的数字串T
T中的1换成S1,把T中的2换成S2,把T中的3换成S3,并输出

abc218_a Weather Forecast

https://atcoder.jp/contests/abc218/tasks/abc218_a
输入n,输出一个长度为n的字符串,问第n个字符是不是o,注意字符串下标从0开始。

abc218_b qwerty

https://atcoder.jp/contests/abc218/tasks/abc218_b
输入26个数字,输出对应的26个字母

abc217_a Lexicographic Order

https://atcoder.jp/contests/abc217/tasks/abc217_a
输入两个字符串S和T,问S<T是否成立,输出Yes或No,字符串之间可以直接比较

abc217_b AtCoder Quiz

https://atcoder.jp/contests/abc217/tasks/abc217_b
输入 ABC, ARC, AGC, AHC 中的三个字符串,输出没输入的字符串

abc216_a Signed Difficulty

https://atcoder.jp/contests/abc216/tasks/abc216_a
输入X.Y,根据Y的范围,输出X-,X,X+三者之一

abc216_b Same Name

https://atcoder.jp/contests/abc216/tasks/abc216_b
输入n个人的姓氏和名字,问是否存在两个人姓氏和名字都相同

abc215_a Your First Judge

https://atcoder.jp/contests/abc215/tasks/abc215_a
输入一个字符串,如果恰好是Hello,World!,输出Yes,否则输出No

abc215_b log2(N)

https://atcoder.jp/contests/abc215/tasks/abc215_b
输入n,找到最大的k,使得2**k<=n

abc215_d Coprime 2

https://atcoder.jp/contests/abc215/tasks/abc215_d
输入n和长度为n的数组,问1到m中哪些数字,和数组中所有数字都互质

abc214_a New Generation ABC

https://atcoder.jp/contests/abc214/tasks/abc214_a
输入n
输出4 如果1<=n<=125
输出6 如果126<=n<=211
输出8 如果212<=n<=214

abc214_b How many?

https://atcoder.jp/contests/abc214/tasks/abc214_b
问有多少组非负整数(a,b,c)满足a+b+c<=Sa*b*c<=T

abc214_d Sum of Maximum Weights

https://atcoder.jp/contests/abc214/tasks/abc214_d
输入一个树,有边权
设f(x,y)表示x和y之间路径上边权的最大值
对于所有(x, y)一共有n*(n-1)/2对,求f(x,y)之和

abc213_a Bitwise Exclusive Or

https://atcoder.jp/contests/abc213/tasks/abc213_a
输入 A 和 B 输出 A 异或 B (A^B)

abc213_b Booby Prize

https://atcoder.jp/contests/abc213/tasks/abc213_b
输入n个数字,输出第2大数字的下标是什么?从1开始下标

abc213_d Takahashi Tour

https://atcoder.jp/contests/abc213/tasks/abc213_d
输入一个树,输出欧拉序,先访问标号较小的孩子

abc212_a Alloy

https://atcoder.jp/contests/abc212/tasks/abc212_a
输入A和B
如果A大于0且B等于0,输出Gold
如果B大于0且A等于0,输出Silver
如果A和B都大于0,输出Alloy

abc212_b Weak Password

https://atcoder.jp/contests/abc212/tasks/abc212_b
输入一个四位数字密码
如果四位一样,或者是正序连续四个整数(9后面接0也算连续)输出Weak
否则输出Strong

abc211_a Blood Pressure

https://atcoder.jp/contests/abc211/tasks/abc211_a
输入A和B,输出(A-B)/3+B,保留至少5位小数

abc211_b Cycle Hit

https://atcoder.jp/contests/abc211/tasks/abc211_b
输入四个字符串,问是不是3B, HR, 2B, H四个字符串,输出YesNo

abc211_c chokudai

https://atcoder.jp/contests/abc211/tasks/abc211_c
输入一个字符串S,问有多少个位置不同的子序列是 chokudai 答案模1000000007输出

abc211_d Number of Shortest paths

https://atcoder.jp/contests/abc211/tasks/abc211_d
输入一个无向图,问从1到N的最短路有多少条?

abc211_e

https://atcoder.jp/contests/abc211/tasks/abc211_e
输入一个N * N的地图
从中选取一个四连通大小为K的区域
问有多少种选法

set vector 爆搜

abc211_f

https://atcoder.jp/contests/abc211/tasks/abc211_f
输入 N 个多边形
Q个询问,询问一个格子被覆盖了多少次

离线树状数组

abc210_a Cabbages

https://atcoder.jp/contests/abc210/tasks/abc210_a
买n个白菜,每个价格是x
如果买够了a个,之后每个价格是y
问需要多少钱?

5 3 20 15
买5个,每个20,但是买第三个以后,每个价格只要15
所以总价格是 20 * 3 + 15 * 2 = 90

10 10 100 1
买10个,每个100,买第10个之后,每个价格只要1
总价格是 100 * 10 = 1000

abc210_b Bouzu Mekuri

https://atcoder.jp/contests/abc210/tasks/abc210_b
输入一个01字符串,问前缀有多少个0
如果有偶数个0,输出Takahashi
如果有奇数个0,输出Aoki

abc210_c Colorful Candies

https://atcoder.jp/contests/abc210/tasks/abc210_c
输出N,K,和长度为N的数组
选择长度为K的区间,希望去年内不同数字的个数最多,问最多多少?

abc210_e Ring MST

https://atcoder.jp/contests/abc210/tasks/abc210_e
一个环形,求MST

Kruskal,GCD

abc209_a Counting

https://atcoder.jp/contests/abc209/tasks/abc209_a
输入A和B,问有多少个数字大于等于A且小于等于B

abc209_b Can you buy them all?

https://atcoder.jp/contests/abc209/tasks/abc209_b
当前有X元钱,买N个商品,第偶数个买的商品可以减一元
问钱够不够,输出YesNo

abc209_c Not Equal

https://atcoder.jp/contests/abc209/tasks/abc209_c
输入N和长度为N的数组C
选择N个数字,要求选择的第i个数字1<=a[i]<=c[i]
且A中不能有相同的数字

abc208_a Rolling Dice

https://atcoder.jp/contests/abc208/tasks/abc208_a
问A个1到6的数字之和是否可能是B,输出Yes或No

abc208_b Factorial Yen Coin

https://atcoder.jp/contests/abc208/tasks/abc208_b
有10种硬币面值,分别为1!,2!,3!,...,10!,其中N!表示N的阶乘
问凑出P的价格,至少需要多少枚硬币

abc207_a Repression

https://atcoder.jp/contests/abc207/tasks/abc207_a
输入3个数字A, B, C问其中较大的两个的和是多少?

abc207_b Hydrate

https://atcoder.jp/contests/abc207/tasks/abc207_b
输入正整数A, B, C, D,问是否存在自然数x使得 A + x * B <= x * C * D
如果存在输出最小的x,如果不存在输出-1

A + x * B <= x * C * D
A <= x * (C * D - B)
A是正数
如果 C * D - B <= 0,那么不可能成立
如果 C * D - B > 0
那么最小的 x 是 A / (C * D - B) 上取整
x / y 上取整 等于 (x + y - 1) / y 下取整

abc206_a Maxi-Buying

https://atcoder.jp/contests/abc206/tasks/abc206_a
输入N,问 1.08 * N下取整 和206的大小关系
如果 1.08 * N下取整 小于 206 输出 Yay!
如果 1.08 * N下取整 等于 206 输出 so-so
如果 1.08 * N下取整 大于 206 输出 :(

abc206_b Savings

https://atcoder.jp/contests/abc206/tasks/abc206_b
第1天存1块钱,第2天存2块钱……第i天存i块钱,
问多少天能存够n的钱。

abc206_c Swappable

https://atcoder.jp/contests/abc206/tasks/abc206_c
输入一个数组,从中选两个下标,满足i<ja[i]!=a[j]
问有多少种选法?

abc206_e Divide Both

https://atcoder.jp/contests/abc206/tasks/abc206_e
问满足条件的对数

容斥原理

abc205_a kcal

https://atcoder.jp/contests/abc205/tasks/abc205_a
每100毫升饮料包含A卡路里能量,问B毫升饮料包含多少卡路里能量,保留至少6位小数

abc205_b Permutation Check

https://atcoder.jp/contests/abc205/tasks/abc205_b
输入一个长度为n的数组,判断是不是1到n的排列。
是1到n排列的充分必要条件是1到n每个数字出现恰好一次。

abc204_a Rock-paper-scissors

https://atcoder.jp/contests/abc204/tasks/abc204_a
你和另外2个人玩石头剪刀布,你知道另外2个人出的是什么,你要让结果变为平局。

abc204_b Nuts

https://atcoder.jp/contests/abc204/tasks/abc204_b
有n个树,每个树上有一些坚果,如果坚果的个数大于10,会拿到只剩10个,否则一个都不拿,问一共拿几个?

abc204_c Tour

https://atcoder.jp/contests/abc204/tasks/abc204_c

abc204_d Cooking

https://atcoder.jp/contests/abc204/tasks/abc204_d
输入N个数字,分成两组,使得和较大的一组,越小越好

abc204_e Rush Hour 2

https://atcoder.jp/contests/abc204/tasks/abc204_e
最短路

需要求 x + d / (t + x) 的最小值

abc204_f Hanjo 2

https://atcoder.jp/contests/abc204/tasks/abc204_f
问覆盖方案数

状态压缩DP,矩阵乘法

abc203_a Chinchirorin

https://atcoder.jp/contests/abc203/tasks/abc203_a
输入三个数字,如果其中有2个相同,输出第三个,如果三个两两不同,输出0。

abc203_b AtCoder Condominium

https://atcoder.jp/contests/abc203/tasks/abc203_b

第i层楼,第j个房间的门牌号是i0j,一共n层楼,每层k个房间,问所有门牌号之和。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
    cin>>n>>m;
    cout<<n*(1+m)*m/2+m*(1+n)*n/2*100;
}

abc202_a Three Dice

https://atcoder.jp/contests/abc202/tasks/abc202_a
三个色子,已知任意相对两面和为7,顶面三个色子分别是A, B, C
问底面三个数字之和是多少

abc202_b 180°

https://atcoder.jp/contests/abc202/tasks/abc202_b
输入一个数字,输出旋转180度之后的结果。

abc201_a Tiny Arithmetic Sequence

https://atcoder.jp/contests/abc201/tasks/abc201_a
输入三个数字,问重新排列之后能不能构成等差数列?

abc201_b Do you know the second highest mountain?

https://atcoder.jp/contests/abc201/tasks/abc201_b
输入N座山的名字和高度,问第二高的山名字是什么?

abc200_a Century

https://atcoder.jp/contests/abc200/tasks/abc200_a
输入n,问是哪个世纪?

abc200_b 200th ABC-200

https://atcoder.jp/contests/abc200/tasks/abc200_b

对n做操作:如果n是200的倍数,/=200,否则在n的数字串末尾加上200三位数字

输入n,和k,输出n做k次操作的结果

#include<bits/stdc++.h>
int main(){
    long long n,k;
    scanf("%lld%lld",&n,&k);
    while(k--){
        if(n%200==0)n/=200;
        else n=n*1000+200;
    }
    printf("%lld\n",n);
}

abc199_a Square Inequality

https://atcoder.jp/contests/abc199/tasks/abc199_a
输入 A,B,C
判断A*A+B*B<C*C是否成立,输出Yes或No

abc199_b Intersection

https://atcoder.jp/contests/abc199/tasks/abc199_b
输入N和两个长度为N的数组A和B
问有多少个x满足x大于等于A中的任意数字且小于等于B中的任意数字

abc199_c IPFL

https://atcoder.jp/contests/abc199/tasks/abc199_c
输入一个长度为2N的字符串,两种操作
Ti=1 交换第Ai和第Bi个字符,下标从1开始
Ti=2 交换字符串的前N个和后N个字符
问最终字符串是什么

abc199_e Permutation

https://atcoder.jp/contests/abc199/tasks/abc199_e
输入N和M,问有多少个1到N的排列满足M个限制条件
第i个限制条件是在排列的前Xi个数字中至多有Zi个数字<=Yi

abc198_a Div

https://atcoder.jp/contests/abc198/tasks/abc198_a
输入一个正整数N,把N分成两个正整数的和,问有多少个方案?

abc198_b Palindrome with leading zeros

https://atcoder.jp/contests/abc198/tasks/abc198_b
输入数字N,可以在开头加上任意个0,问能不能让这个数字变成回文数?

abc197_a Rotate

https://atcoder.jp/contests/abc197/tasks/abc197_a
输入三个字母,把第一个字母挪到最后,输出这三个字母

abc197_b Visibility

https://atcoder.jp/contests/abc197/tasks/abc197_b
输入一个二维数组,表示空地还是障碍物,问从起点往上下左右看,能看到多少个格子?

abc196_a Difference Max

https://atcoder.jp/contests/abc196/tasks/abc196_a
输入a b c d
已知a<=x<=b并且c<=y<=d
x-y最大是多少?

abc196_b Round Down

https://atcoder.jp/contests/abc196/tasks/abc196_b
输入非负实数X,输出X下取整
X的整数部分和小数部分都可能有100位

abc195_a Health M Death

https://atcoder.jp/contests/abc195/tasks/abc195_a
输入M和H,问H是否是M的倍数

abc195_b Many Oranges

https://atcoder.jp/contests/abc195/tasks/abc195_b
输入A,B,W,每个橘子的质量在A克和B克之间,已知若干个橘子重量是W千克

问至多多少个,至少多少个,如果无解输出UNSATISFIABLE

abc194_a I Scream

https://atcoder.jp/contests/abc194/tasks/abc194_a
输入数量为A的 milk solids-not-fat,和数量为B的 milk fat
milk solids的数量是 milk fat 和 milk solids-not-fat 相加
如果 milk solids >= 15 且 milk fat >= 8 输出 1
如果 milk solids >= 10 且 milk fat >= 3 输出 2
如果 milk solids >= 3 输出 3
如果以上都不满足 输出 4

abc194_b Job Assignment

https://atcoder.jp/contests/abc194/tasks/abc194_b
一共A和B两个任务,n个人,每个人完成任务有时间Ai和Bi
可以把这两个任务分给某两个人或者是一个人,希望总完成时间最短
分别找到两个数组的最快和次快
如果两个最快不是同一人,直接输出答案
如果两个最快是同一人,分三种情况讨论

abc193_a Discount

https://atcoder.jp/contests/abc193/tasks/abc193_a

输入原价格A,和现价格B,问折扣了百分之多少?保留至少两位小数

#include <bits/stdc++.h>
using namespace std;
int main() {
    double a, b;
    cin >> a >> b;
    printf("%.6f\n", (1 - b / a) * 100);
    return 0;
}

abc193_b Play Snuke

https://atcoder.jp/contests/abc193/tasks/abc193_b
n个商店,走到第i个商店需要Ai分钟,价格是Pi,库存是Xi
如果Xi<=Ai这个店会在自己走到之前卖完
需要选一个有存货的商店买一个商品,问至少需要多少钱
如果买不到输出-1

abc193_c Unexpressed

https://atcoder.jp/contests/abc193/tasks/abc193_c
输入N,问1到N中有多少个数字不是能表示为 某个数字的某次幂
某个数字的某次幂 要求指数和底数都至少是2

abc192_a Star

https://atcoder.jp/contests/abc192/tasks/abc192_a
输入x,问至少将x增加多少,才能使得结果是100的倍数,不能不增加。

abc192_b uNrEaDaBlE sTrInG

https://atcoder.jp/contests/abc192/tasks/abc192_b
输入一个字符串,从0开始下标,问是不是所有奇数位的字母都是小写且所有偶数位的字母都是大写
输出YesNo

abc191_a Vanishing Pitch

https://atcoder.jp/contests/abc191/tasks/abc191_a
输入V,T,S,D,V是速度,D是路程,问时间是否在T和S之间,不在输出Yes,在输出No

abc191_b Remove It

https://atcoder.jp/contests/abc191/tasks/abc191_b

输入n,x,和一个长度为n的数组A,把A中的x都删掉,输出剩下的数字保持原序。

#include <bits/stdc++.h>
using namespace std;
int main() {int X, A; for (cin >> X >> X; cin >> A;) if (A - X) cout << A << " ";}

abc190_a Very Very Primitive Game

https://atcoder.jp/contests/abc190/tasks/abc190_a
Takahashi 有 A 个糖
Aoki 有 B 个糖
两个人轮流吃糖
如果 C=0 那么 Takahashi 先吃
如果 C=1 那么 Aoki 先吃
谁先没糖吃,谁就输了
问谁能赢

abc190_b Magic 3

https://atcoder.jp/contests/abc190/tasks/abc190_b
n个技能,每个技能施法时间Xi,伤害Yi,问有没有施法时间<S且伤害>D的技能

#include <bits/stdc++.h>
using namespace std;
int n, s, d, x, y;
int main()
{
    cin >> n >> s >> d;
    for (int i = 0; i < n; i++)
    {
        cin >> x >> y;
        if (x < s && y > d)
        {
            cout << "Yes" << endl;
            return 0;
        }
    }
    cout << "No" << endl;
    return 0;
}

abc190_c Bowls and Dishes

https://atcoder.jp/contests/abc190/tasks/abc190_c
N个盘子,M个条件,第i个条件是盘子Ai和盘子Bi上都有球
K(K<=16)个人, 第i个人可以往盘子Ci或盘子Di上放一个球,二选一
问至多满足多少个条件?

abc189_a Slot

https://atcoder.jp/contests/abc189/tasks/abc189_a
输入三个字母,如果都一样,输出Won,否则输出Lost

abc189_b Alcoholic

https://atcoder.jp/contests/abc189/tasks/abc189_b
n杯酒,一杯一杯喝,每一杯有容量V和浓度P,问喝到第几杯,已经喝的酒精>X

abc189_c Mandarin Orange

https://atcoder.jp/contests/abc189/tasks/abc189_c
输入N,和一个长度为N的数组,选择区间L,R
最大化(R-L+1)*L到R区间的最小值

abc188_a Three-Point Shot

https://atcoder.jp/contests/abc188/tasks/abc188_a
输入篮球赛比分,保证不是平局
问落后方+3分能不能反超?输出Yes或No
abs(x) 表示 x 的绝对值

abc188_b Orthogonality

https://atcoder.jp/contests/abc188/tasks/abc188_b

输入两个向量,问是否正交,正交的定义看题目或代码。

#include<stdio.h>
int a[100001],b[100001],j,n,c=0;
int main()
{
    scanf("%d",&n);
    for(j=0;j<n;j++)
    scanf("%d",&b[j]);
    for(j=0;j<n;j++)
    {scanf("%d",&a[j]);
    c=a[j]*b[j]+c;}
    if(c!=0)
    printf("No");
    else printf("Yes");
    return 0;
}

abc188_c ABC Tournament

https://atcoder.jp/contests/abc188/tasks/abc188_c
输入N,有2的次方个人,每个人有一个能力值Ai,能力值互不相同
N个人打淘汰赛,问谁是第二名

abc188_d Snuke Prime

https://atcoder.jp/contests/abc188/tasks/abc188_d
有n个服务需要用,第i个服务需要从第ai天用到第bi天,每天ci元
如果购买Snuke Prime只需要付每天C元,便可以用所有服务(不需要付每个服务的ci)
问至少花多少钱?

abc187_a Large Digits

https://atcoder.jp/contests/abc187/tasks/abc187_a
输入两个数字,问各个数位之和较大的和是多少。

abc187_b Gentle Pairs

https://atcoder.jp/contests/abc187/tasks/abc187_b
输入N个点,从中选两个点i和j(i<j)
问有多少种选法,使得经过这两个点的斜线的斜率在-1和1之间

abc186_a Brick

https://atcoder.jp/contests/abc186/tasks/abc186_a
限重N公斤,一块砖W公斤,问能带几块砖

abc186_b Blocks on Grid

https://atcoder.jp/contests/abc186/tasks/abc186_b

输入H行W列的二维数组,第i行第j列的数组Aij表示这个位置有Aij个砖。

至少删掉多少个砖可以让所有位置的砖的数目相同?

#include <bits/stdc++.h>
using namespace std;
int main(){
    int w,h;cin>>h>>w;
    int c=w*h;
    int m=101,s=0;
    for(int i=0;i<c;i++){
        int a;cin>>a;
        m=min(a,m);
        s+=a;
    }
    cout<<s-m*c<<endl;
}

abc186_c Unlucky 7

https://atcoder.jp/contests/abc186/tasks/abc186_c
问1到N中有多少个数字,十进制和八进制中都没有7

abc186_d Sum of difference

https://atcoder.jp/contests/abc186/tasks/abc186_d
输入N个数字,问任意两个数字差的绝对值之和

abc185_a ABC Preparation

https://atcoder.jp/contests/abc185/tasks/abc185_a
输入4个数字,输出其中最小的是多少?

abc185_b Smartphone Addiction

https://atcoder.jp/contests/abc185/tasks/abc185_b
在时间0,手机电量是N,在时间n+0.5手机电量会增加或减少一
其中有M个区间会增加一,其他时间会减少一
手机电量上限是N
问能不能在T时间还有电

abc185_c Duodecim Ferra

https://atcoder.jp/contests/abc185/tasks/abc185_c
N个相同的球,插入11个隔板,分成12个非空的组,问方案数?

abc184_a Determinant

https://atcoder.jp/contests/abc184/tasks/abc184_a
输入 a, b, c, d 四个整数,输出 a * d - b * c

abc184_b Quizzes

https://atcoder.jp/contests/abc184/tasks/abc184_b
初始X分,输入一个字符串S,遇到x减一分,遇到o加一分,如果已经是0分了就不减分,问最终得分

abc184_c Super Ryuma

https://atcoder.jp/contests/abc184/tasks/abc184_c

问需要几步

最多三步

abc184_d increment of coins

https://atcoder.jp/contests/abc184/tasks/abc184_d
期望DP

abc183_a ReLU

https://atcoder.jp/contests/abc183/tasks/abc183_a
输入x,如果 x >= 0 那么输出 x,如果 x < 0 那么输出 0

abc183_b Billiards

https://atcoder.jp/contests/abc183/tasks/abc183_b
输入坐标(sx,sy)瞄准x轴发射,反弹,击中(gx,gy)问应该瞄准的位置的x坐标是多少

abc183_c Travel

https://atcoder.jp/contests/abc183/tasks/abc183_c
有N个城市,已知两两之间的距离,
希望从城市1出发,访问每个城市恰好一次,回到城市1
问有多少种排列可以让时间恰好是K?

abc182_a twiblr

https://atcoder.jp/contests/abc182/tasks/abc182_a
在社交网站 twiblr 上,一个人可以关注 2 * 关注这个人的人数+100 个人
现在,有A个人关注你,你关注了B个人,问还能关注几个人?

abc182_b Almost GCD

https://atcoder.jp/contests/abc182/tasks/abc182_b
输入一个长度为N的数组,A1, A2, .., AN
输出一个数字,使得输出的数字是尽可能多个数字的约数,如果多解输出任意一个

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n, a[100];
    int m = 0, r = -1, c = 0;
    cin >> n;
    for(int i = 0; i < n; i++){
        cin >> a[i];
    }
    for(int i = 2; i <= 1000; i++){
        c = 0;
        for(int j = 0; j < n; j++){
            if(a[j] % i == 0){
                c++;
            }
        }
        if(m < c){
            m = c;
            r = i;
        }
    }
    cout << r << endl;
    return 0;
}

abc182_c To 3

https://atcoder.jp/contests/abc182/tasks/abc182_c
输入一个数字,删去其中若干位数字,让结果变成3的倍数,问至少删几位
不能全删,如果无解输出-1

abc181_a Heavy Rotation

https://atcoder.jp/contests/abc181/tasks/abc181_a
输入N,如果N是偶数,输出White,如果N是奇数,输出Black

abc181_b Trapezoid Sum

https://atcoder.jp/contests/abc181/tasks/abc181_b
输入N个等差数列,第i个等差数列,首项是ai,末项是bi,公差是1
问所有等差数列之和是多少?

abc181_c Collinearity

https://atcoder.jp/contests/abc181/tasks/abc181_c
输入N个点,问是否存在三点共线?输出YesNo

abc180_a box

https://atcoder.jp/contests/abc180/tasks/abc180_a
盒子里有N个球,从中拿出来A个,放进去B个,问现在有几个球?

abc180_b Various distances

https://atcoder.jp/contests/abc180/tasks/abc180_b
输入一个n维空间内的点,输出到原点的
曼哈顿距离,所有维度坐标绝对值之和
欧几里得距离,所有维度平方求和开根号
切比雪夫距离,所有维度坐标绝对值的最大值

abc180_c Cream puff

https://atcoder.jp/contests/abc180/tasks/abc180_c
输入n,升序输出n的所有约数

abc179_a Plural Form

https://atcoder.jp/contests/abc179/tasks/abc179_a
输入一个字符串,如果以s结尾,结尾加es,否则加s。

abc179_b Go to Jail

https://atcoder.jp/contests/abc179/tasks/abc179_b
每次扔两个骰子,扔n次,输入扔的结果
问是否存在连续三次,每次的两个骰子数字一样(但是这三次不需要一样)
输出YesNo

abc179_c A x B + C

https://atcoder.jp/contests/abc179/tasks/abc179_c
输入N问有多少组正整数(A,B,C)满足A*B+C==N?

abc178_a Not

https://atcoder.jp/contests/abc178/tasks/abc178_a
输入 x 保证是0或者1,如果输入0,那么输出1,如果输入1,那么输出0

abc178_b Product Max

https://atcoder.jp/contests/abc178/tasks/abc178_b
已知xy的范围是a<=x<=b,c<=y<=d
x*y最大是多少?

abc177_a Don't be late

https://atcoder.jp/contests/abc177/tasks/abc177_a
一个人速度是 S 米每秒,问 T 秒能不能走 D 米的距离。
参考错误代码:

#include <bits/stdc++.h>
int main()
{
    int D, T, S;
    cin >> D >> T >> S;
    if (D / S <= T) // 这里有错,C++中除法默认是下取整,应为 D<=S*T 或 (D+S-1)/S<=T
    {
        cout << "Yes" << endl;
    }
    else
    {
        cout << "No" << endl;
    }
    return 0;
}

abc177_b Substring

https://atcoder.jp/contests/abc177/tasks/abc177_b
输入两个字符串S和T
问在S中至少修改几个字符,才可以使得T是S的字串

abc177_c Sum of product of pairs

https://atcoder.jp/contests/abc177/tasks/abc177_c
输入N个数字,问其中所有无序对的乘积之和是多少?

abc177_d Friends

https://atcoder.jp/contests/abc177/tasks/abc177_d
有n个人,m个关系,关系有传递性
问n个人至少分成多少个集合
才能让每个集合内的人之间都没有关系

abc177_e Coprime

https://atcoder.jp/contests/abc177/tasks/abc177_e
输入一个数组,问是两两互质,还是所有数字gcd为1,还是所有数字gcd大于1

abc176_a Takoyaki

https://atcoder.jp/contests/abc176/tasks/abc176_a
做一次可以做X个零食,需要T的时间。问做N个零食需要多久?
除法上取整

abc176_b Multiple of 9

https://atcoder.jp/contests/abc176/tasks/abc176_b

输入n,问n是不是9的倍数,n最多有200000位。

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s;
    cin>>s;
    int u=0;
    for(char c:s)u+=c-'0';
    cout<<(u%9?"No":"Yes");
}

abc176_c Step

https://atcoder.jp/contests/abc176/tasks/abc176_c
输入N个人的高度,每个人都希望通过增加自己的高度,使得自己的高度大于等于自己左边的人的高度
问总共至少增加多少高度,才能满足每个人的愿望

abc175_a Rainy Season

https://atcoder.jp/contests/abc175/tasks/abc175_a
输入一个长度为3,包含R和S的字符串,问最多连续几个R?分8种情况讨论。

abc175_b Making Triangle

https://atcoder.jp/contests/abc175/tasks/abc175_b
输入N个数字,从中选取三个数字构成一个三角形,问有多少种方案?

abc175_c Walking Takahashi

https://atcoder.jp/contests/abc175/tasks/abc175_c
初始在X的位置上,每一步走D的距离,可以向前或向后,必须走K步
问K步之后距离原点最近多近?

abc174_a Air Conditioner

https://atcoder.jp/contests/abc174/tasks/abc174_a
输入一个温度X,问是否>=30,输出YesNo

abc174_b Distance

https://atcoder.jp/contests/abc174/tasks/abc174_b
输入n个点的坐标(xi,yi),问其中有多少个点到原点的距离<=D

abc174_c Repsept

https://atcoder.jp/contests/abc174/tasks/abc174_c
输入K,问最小连续几个7组成的数字,是K的倍数?无解输出-1

abc174_d Alter Altar

https://atcoder.jp/contests/abc174/tasks/abc174_d
输入一个长度为n的WR序列
一次操作可以交换两个字符,或者修改一个字符
不希望出现WR子串(也就是说最后修改的结果,一定是所有R在所有W之前)
问最少几次操作

abc173_a Payment

https://atcoder.jp/contests/abc173/tasks/abc173_a
买N元的东西,用1000元的纸币,问最少找多少钱

abc173_b Judge Status Summary

https://atcoder.jp/contests/abc173/tasks/abc173_b
输入n个字符串,每个一定是AC,WA,TLE,RE中的一个
问每种字符串出现多少次?0次也要输出

abc172_a Calc

https://atcoder.jp/contests/abc172/tasks/abc172_a
输入 a,输出 a + a * a + a * a * a

abc172_b Minor Change

https://atcoder.jp/contests/abc172/tasks/abc172_b
输入长度相等的两个字符串ST
每次操作选择S中的一个字母,并替换为另一个不同的字母
问至少多少次操作可以使得ST相等

abc172_d Sum of Divisors

https://atcoder.jp/contests/abc172/tasks/abc172_d
1到n每个数字i的约数个数,再乘以i,求和

abc171_a αlphabet

https://atcoder.jp/contests/abc171/tasks/abc171_a
输入一个字母,如果是大写,输出A,如果是小写,输出a

abc171_b Mix Juice

https://atcoder.jp/contests/abc171/tasks/abc171_b
输入n个数字,问较小的k个之和是多少?

abc171_c One Quadrillion and One Dalmatians

https://atcoder.jp/contests/abc171/tasks/abc171_c
输入一个数字,转成字符串

abc171_d Replacing

https://atcoder.jp/contests/abc171/tasks/abc171_d
维护一个数字,支持把所有的Bi改成Ci,和求数组中所有数字之和

abc170_a Five Variables

https://atcoder.jp/contests/abc170/tasks/abc170_a
输入5个变量
本应该是 1 2 3 4 5,但其中一个被改成0了
问是哪一个

输入 1 2 0 4 5
输出 3

输入 0 2 3 4 5
输出 1

abc170_b Crane and Turtle

https://atcoder.jp/contests/abc170/tasks/abc170_b
鸡兔同笼问题,x个头,y个脚,问有没有解,只需要输出有没有解,Yes或No

abc170_c Forbidden List

https://atcoder.jp/contests/abc170/tasks/abc170_c
输入X和N个整数Ai
问离X最接近,且没在这N个整数中出现的数字是多少?
如果有多个最接近的,输出最小的

abc169_a Multiplication 1

https://atcoder.jp/contests/abc169/tasks/abc169_a
输入A和B,输出A*B

abc169_b Multiplication 2

https://atcoder.jp/contests/abc169/tasks/abc169_b
输入N个数字,输出他们的乘积,如果乘积大于1e18输出-1

abc169_c Multiplication 3

https://atcoder.jp/contests/abc169/tasks/abc169_c
输入A和B,输出他们的乘积,取整
其中A是整数,B至多两位小数

abc168_a ∴ (Therefore)

https://atcoder.jp/contests/abc168/tasks/abc168_a
输入一个数字
如果个位是 24579 输出 hon
如果个位是 0168 输出 pon
如果个位是 3 输出 bon

abc168_b ... (Triple Dots)

https://atcoder.jp/contests/abc168/tasks/abc168_b
输入长度K和一个字符串
如果字符串的长度小于等于K,那么直接输出
如果字符串的长度大于K,那么输出前K个字符和...

abc167_a Registration

https://atcoder.jp/contests/abc167/tasks/abc167_a
输入两个字符串,问第一个是不是第二个的前缀
用substr

abc167_b Easy Linear Programming

https://atcoder.jp/contests/abc167/tasks/abc167_b
A1B0C-1,从中选取K个数字,问和最大是多少?

abc166_a A?C

https://atcoder.jp/contests/abc166/tasks/abc166_a
输入ABC和ARC之一,输出另一个

abc166_b Trick or Treat

https://atcoder.jp/contests/abc166/tasks/abc166_b
第一行N和K,表示N个小朋友,种个玩具
接下来K段,第i段表示哪些小朋友有第i个玩具,包含两行
第一行表示拥有这个玩具的小朋友个数di,第二行di个数字,表示具体是哪些小朋友
输出有几个小朋友没有任何玩具

abc165_a We Love Golf

https://atcoder.jp/contests/abc165/tasks/abc165_a
输入K, A, B。问A和B之间有没有K的倍数

abc165_b 1%

https://atcoder.jp/contests/abc165/tasks/abc165_b

刚开始有100日元,每年的利息是1%,不足1日元不发钱,问多少年可以>=X日元?

#include <stdio.h>
long long x,a=100,ans;
main(){
    scanf("%lld",&x);while(a<x) a+=a/100,ans++;printf("%lld\n",ans);return 0;
}

abc164_a Sheep and Wolves

https://atcoder.jp/contests/abc164/tasks/abc164_a

输入 S 和 W,如果 W >= S 输出 unsafe 否则输出 safe

#include <bits/stdc++.h> 
using namespace std;
int main()
{
    int s,w;
    cin>>s>>w;
    if (w >= s)
    {
        cout << "unsafe" << endl;
    }
    else
    {
        cout << "safe" << endl;
    }
}

abc164_b Battle

https://atcoder.jp/contests/abc164/tasks/abc164_b
Takahashi 的生命值是A,攻击力是B
Aoki 的生命值是C,攻击力是D
双方轮流攻击,Takahashi 先攻击,直到一方的生命值小于等于0,此时生命值大于0的为获胜方
问 Takahashi 能不能赢?输出YesNo

abc163_a Circle Pond

https://atcoder.jp/contests/abc163/tasks/abc163_a
输入圆的半径,输出圆的周长,保留至少两位小数

abc163_b Homework

https://atcoder.jp/contests/abc163/tasks/abc163_b
假期一共有N天,有M个作业要做,第i个作业花费Ai天完成
问能剩下多少天玩?如果做不完输出-1,如果恰好做完,没有时间玩,输出0

abc163_c management

https://atcoder.jp/contests/abc163/tasks/abc163_c
N个人,第1个人是大老板
除此之外每个人有上司ai
问每个人有几个下属?

abc162_a Lucky 7

https://atcoder.jp/contests/abc162/tasks/abc162_a
输入一个三位数,问是否包含7,输出Yes或No

abc162_b FizzBuzz Sum

https://atcoder.jp/contests/abc162/tasks/abc162_b
输入n,求1到n中既不是3的倍数,也不是5的倍数的所有数字之和
两个做法,直接枚举,容斥原理

abc162_c FizzBuzz Sum

https://atcoder.jp/contests/abc162/tasks/abc162_c
输入k
枚举a从1到k
枚举b从1到k
枚举c从1到k
计算gcd(a,b,c)的和

abc161_a ABC Swap

https://atcoder.jp/contests/abc161/tasks/abc161_a
输入三个数字X, Y, Z
交换X和Y,交换X和Z
输出X, Y, Z

https://atcoder.jp/contests/abc161/tasks/abc161_b
n个商品被投票,第i个的票数是Ai
问能不能选出m个商品,使得选出的商品的票数都大于等于 总票数/(4m)

abc161_c Replacing Integer

https://atcoder.jp/contests/abc161/tasks/abc161_c
输入NK
每次操作可以把N改为|N-K|N和K差的绝对值
你可以做任意次操作,也可以不做,希望最终结果越小越好
问最小是多少?

abc160_a Coffee

https://atcoder.jp/contests/abc160/tasks/abc160_a
输入一个字符串,判断是否第三个字符等于第四个字符,且第五个字符等于第六个字符,输出Yes或No

abc160_b Golden Coins

https://atcoder.jp/contests/abc160/tasks/abc160_b
现在有X元,500元可以买1000的幸福值,5元可以买5的幸福值,问最多可以买多少幸福值?

abc160_c Traveling Salesman around Lake

https://atcoder.jp/contests/abc160/tasks/abc160_c
环形池塘上有N个点
从其中任意一个点出发,向左或向右走,到任意一个点结束
要求N个点都到过
问最小距离是多少?

abc159_a The Number of Even Pairs

https://atcoder.jp/contests/abc159/tasks/abc159_a
N个偶数,M个奇数,从中选2个,和是偶数,问方案数

abc159_b String Palindrome

https://atcoder.jp/contests/abc159/tasks/abc159_b
一个长度为奇数N的字符串被称为强回文,当且仅当
本身是回文串,第1到第(N-1)/2个字符是回文,第(N+3)/2到第N个字符是回文
输入字符串S,问是不是强回文

abc159_c Maximum Volume

https://atcoder.jp/contests/abc159/tasks/abc159_c
输入L,问长宽高之和为L的立方体,体积最大是多少?

abc158_a Station and Bus

https://atcoder.jp/contests/abc158/tasks/abc158_a
输入一个长度为3的字符串,问是不是既有A,又有B

abc158_b Count Balls

https://atcoder.jp/contests/abc158/tasks/abc158_b
A个蓝色,B个红色,A个蓝色,B个红色,A个蓝色,B个红色……循环
问前n个中,有多少个蓝色
n/(A+B)*A + min(n%(A+B),A)

abc158_c Tax Increase

https://atcoder.jp/contests/abc158/tasks/abc158_c
输入A和B
找到最小的N,使得N*0.08下取整==A并且N*0.1下取整==B
如果无解输出-1

abc157_a Duplex Printing

https://atcoder.jp/contests/abc157/tasks/abc157_a
输入 n,双面打印一个n页的文件,问需要几张纸?输出 (n+1)/2

abc157_b Bingo

https://atcoder.jp/contests/abc157/tasks/abc157_b
输入一个3x3的数组aij,接下来输入n和n个数字bi
如果bi在数组a中出现,就标记上出现的位置
如果被标记的位置形成了一行三个,一列三个,或者对角线三个,输出Yes,否则输出No
保证数组a中数字互不相同,数组b中数字互不相同

abc156_a Beginner

https://atcoder.jp/contests/abc156/tasks/abc156_a
输入N和R,输出R+(10-N)*100

abc156_b Digits

https://atcoder.jp/contests/abc156/tasks/abc156_b
输入n和k,问n在k进制下有几位?

#include <iostream>
using namespace std;
int n,a,ans;
int main(){
    cin>>n>>a;
    for(;n;n/=a)ans++;
    cout<<ans;
    return 0;
}

abc156_c Rally

https://atcoder.jp/contests/abc156/tasks/abc156_c
数轴上N个人要聚会,第i个人初始位置是Xi
如果选择在P聚会,第i个人的代价是(Xi-P)**2
必须在整数位置聚会
问总代价之和最小是多少

abc155_a Poor

https://atcoder.jp/contests/abc155/tasks/abc155_a
输入三个数字A,B,C,如果三个数字中有且只有两个相同,那么输出Yes,如果两两不同或者都相同输出No

abc155_b Papers, Please

https://atcoder.jp/contests/abc155/tasks/abc155_b
输入一个数组,如果所有偶数都是3或5的倍数,输出APPROVED
否则输出DENIED

abc155_c Poll

https://atcoder.jp/contests/abc155/tasks/abc155_c
输入n个字符串,问出现次数最多的字符串有哪些?按字典序输出

abc155_d Pairs

https://atcoder.jp/contests/abc155/tasks/abc155_d
选两个数字乘积,问第K大是什么?

二分

abc154_a Remaining Balls

https://atcoder.jp/contests/abc154/tasks/abc154_a
输入A个S色的球,B个T色的球
拿走一个U色的球
问S色的球和T色的球还剩下几个(其中有且只有一种颜色会减少一个)

abc154_b I miss you...

https://atcoder.jp/contests/abc154/tasks/abc154_b
输入一个字符串S
把每个字符都替换成x
输出

abc154_d Dice in Line

https://atcoder.jp/contests/abc154/tasks/abc154_d
选K个,和最大

abc153_a Serval vs Monster

https://atcoder.jp/contests/abc153/tasks/abc153_a
一个怪物初始生命值H,被攻击一次生命值减少A,如果怪物生命值小于等于零玩家胜利
问需要攻击几次

abc153_b Common Raccoon vs Monster

https://atcoder.jp/contests/abc153/tasks/abc153_b
输入h,n和n个数字,问这n个数字的和是否>=h,输出Yes或No

abc153_c Fennec vs Monster

https://atcoder.jp/contests/abc153/tasks/abc153_c
打怪练级,一共n个怪物,第i个怪物生命值是Hi
攻击一次,生命值减少1。生命值变为0,怪物消失
特殊技能可以直接让一个怪物消失,但只能使用k次
问至少需要攻击多少次,才能消灭所有怪物

abc153_d Caracal vs Monster

https://atcoder.jp/contests/abc153/tasks/abc153_d
打怪练级
如果怪物生命值是1,攻击之后会消失
如果怪物生命值是X,攻击之后会变成两个X/2下取整的怪物
初始只有一个怪物生命值为H,问需要攻击多少次让怪物消失

abc152_a AC or WA

https://atcoder.jp/contests/abc152/tasks/abc152_a
输入N和M,如果N==M输出Yes,如果N>M输出No

abc152_b Comparing Strings

https://atcoder.jp/contests/abc152/tasks/abc152_b
输入两个数字ab
问a个b和b个a哪个字典序更小?
输出字典序较小的

abc152_c Low Elements

https://atcoder.jp/contests/abc152/tasks/abc152_c
输入一个排列,从左向右看,问有多少个到目前为止的最小值

abc151_a Next Alphabet

https://atcoder.jp/contests/abc151/tasks/abc151_a
输入一个小写字母,输出下一个字母,不会输入z
输入一个不是z的字母,输出下一个字母是什么。

ASCII码

abc151_b Achieve the Goal

https://atcoder.jp/contests/abc151/tasks/abc151_b
一共N门课,每门课的分数是0到K的一个数字
已知前N-1门课的分数,问最后一门课至少多少分,才能使得所有课平均分>=M
如果做不到输出-1

abc150_a 500 Yen Coins

https://atcoder.jp/contests/abc150/tasks/abc150_a
如果500*K>=X,输出Yes,否则输出No

abc150_b Count ABC

https://atcoder.jp/contests/abc150/tasks/abc150_b
输入一个字符串,问ABC作为子串出现过多少次?

abc149_a Strings

https://atcoder.jp/contests/abc149/tasks/abc149_a
输入两个字符串S和T,输出T和S,中间不加空格

abc149_b Greedy Takahashi

https://atcoder.jp/contests/abc149/tasks/abc149_b
输入A, B, K,执行以下操作K次
如果A大于零,那么自减一;否则如果B大于零,那么B自减一;否则什么都不做
问执行操作后A和B是多少?

abc149_c Next Prime

https://atcoder.jp/contests/abc149/tasks/abc149_c
输入 x,输出大于等于x最小的质数是多少

abc148_a Round One

https://atcoder.jp/contests/abc148/tasks/abc148_a
三个选项1, 2, 3
输入两个错误选项,问正确的是哪个?

abc148_b Strings with the Same Length

https://atcoder.jp/contests/abc148/tasks/abc148_b
输入两个长度为n的字符串,交替输出两个字符串的内容

abc148_d Brick Break

https://atcoder.jp/contests/abc148/tasks/abc148_d
输入一个数组,删掉一些数字
使得剩下的数字是1, 2, 3, ...
问至少删多少个数字?
如果必须全删,输出-1

abc147_a Blackjack

https://atcoder.jp/contests/abc147/tasks/abc147_a
输入A1,A2,A3,如果和大于等于22,输出bust,否则输出win

abc147_b Palindrome-philia

https://atcoder.jp/contests/abc147/tasks/abc147_b
输入一个字符串,问最少修改几个字符,可以让这个串变成回文串

abc147_c HonestOrUnkind2

https://atcoder.jp/contests/abc147/tasks/abc147_c
输入N个人,每个人可能是真人或者是假人,真人一定说真话,假人可能说真话可能说假话
他们会说其他人是真人还是假人,问其中至多有多少个真人

abc147_d Xor Sum 4

https://atcoder.jp/contests/abc147/tasks/abc147_d
输入N个数字,从中选两个数字做按位异或,问所有结果和是多少?
按位讨论

abc146_a Can't Wait for Holiday

https://atcoder.jp/contests/abc146/tasks/abc146_a
输入星期中的一天,问到下周日还有几天

abc146_b ROT N

https://atcoder.jp/contests/abc146/tasks/abc146_b
输入n和大写字母字符串,每个字母往后挪n位,问字符串会变成什么
n<=26
Z往后挪一位会变成A

abc146_c Buy an Integer

https://atcoder.jp/contests/abc146/tasks/abc146_c

abc146_d Coloring Edges on Tree

https://atcoder.jp/contests/abc146/tasks/abc146_d
输入一个N个点N-1条边的树,每条边染色,要求有公共端点的边颜色必须不同
问至少需要多少种颜色,并给出一个方案

abc145_a Circle

https://atcoder.jp/contests/abc145/tasks/abc145_a
输入半径,问面积是多少倍的Pi

abc145_b Echo

https://atcoder.jp/contests/abc145/tasks/abc145_b
输入一个字符串,问这个字符串能不能由一个字符串重复两次得到,输出Yes或No

abc145_d Knight

https://atcoder.jp/contests/abc145/tasks/abc145_d
如果当前在(i,j),那么下一步可以走到(i+1,j+2)(i+2,j+1)
(0,0)走到(x,y),问有多少种方案?
答案模1000000007

abc144_a 9x9

https://atcoder.jp/contests/abc144/tasks/abc144_a
输入两个正整数,如果都小于等于9,输出他们的乘积,否则输出-1

abc144_b 81

https://atcoder.jp/contests/abc144/tasks/abc144_b
输入一个数字,问是不是两个1~9的数字的乘积
输出Yes或No

abc144_c Walk on Multiplication Table

https://atcoder.jp/contests/abc144/tasks/abc144_c
一个无限大的乘法表,第i行第j列的数字是i*j
初始在第一行第一列,每一步可以往右一列或者往下一行
输入n问至少几步可以走到数字是n的格子

abc143_a Curtain

https://atcoder.jp/contests/abc143/tasks/abc143_a
长度为A的窗户,用两个长度为B的覆盖,问未覆盖的长度是多少

abc143_b TAKOYAKI FESTIVAL 2019

https://atcoder.jp/contests/abc143/tasks/abc143_b
输入n个数字,我们都知道从n个数字中选两个有n*(n+1)/2个方案
对于这n*(n+1)/2个方案,选出的2个数字计算乘积
问这n*(n+1)/2个乘积的和是多少?

abc142_a Odds of Oddness

https://atcoder.jp/contests/abc142/tasks/abc142_a
输入n,1到n随机一个整数,问奇数的概率,保留至少6位小数

abc142_b Roller Coaster

https://atcoder.jp/contests/abc142/tasks/abc142_b
输入N个数字,问其中有多少个>=K

abc142_d Disjoint Set of Common Divisors

https://atcoder.jp/contests/abc142/tasks/abc142_d
输入 a 和 b,输出 gcd(a,b) 不同质因数的个数

abc141_a Weather Prediction

https://atcoder.jp/contests/abc141/tasks/abc141_a
已知天气按照Sunny, Cloudy, Rainy, Sunny, Cloudy, Rainy, ...
输入今天的天气,输出明天的天气

abc141_b Tap Dance

https://atcoder.jp/contests/abc141/tasks/abc141_b
输入字符串,问是否所有奇数位置都是RUD之一,且偶数位置都是LUD之一
奇偶都是从1开始数的

abc140_a Password

https://atcoder.jp/contests/abc140/tasks/abc140_a
输入 n,问只包含1到n的三位数有几个?输出 n * n * n

abc140_b Buffet

https://atcoder.jp/contests/abc140/tasks/abc140_b
一个人吃N道菜,第i次吃第Ai盘菜
吃掉第i盘才收益Bi
吃掉第i盘之后立刻吃第i+1盘额外收益Ci
问总收益多少

abc140_c Maximal Value

https://atcoder.jp/contests/abc140/tasks/abc140_c
输入长度为N-1的B
有一个长度为N的A
要求B[i]>=max(A[i],A[i+1])
问A中所有元素之和最大是多少

abc139_a Tenki

https://atcoder.jp/contests/abc139/tasks/abc139_a
输入两个由CSR三种字母组成的长度为3的字符串
问其中有几个对应的位置相等

abc139_b Power Socket

https://atcoder.jp/contests/abc139/tasks/abc139_b
刚开始只有1个插口,一个插线板可以让1个插口变成A个插口
问至少多少个插线板,才能有B个插口

abc139_d ModSum

https://atcoder.jp/contests/abc139/tasks/abc139_d
输入n,构造一个1到n的排列P[1],P[2],...,P[n]
1到n每个数字出现恰好一次
M[i]=P[i]%i
M[1]+M[2]+M[3]+...+M[n]最大是多少?

abc138_a Red or Not

https://atcoder.jp/contests/abc138/tasks/abc138_a
输入数字a和字符串s
如果a小于3200输出red否则输出字符串s

abc138_b Resistors in Parallel

https://atcoder.jp/contests/abc138/tasks/abc138_b
输入n个数字,问这n个数字倒数之和的倒数是多少

abc138_c Alchemist

https://atcoder.jp/contests/abc138/tasks/abc138_c
输入N个数字,合并N-1次,合并两个数字会变成这两个数字的平均值
希望结果越大越好,问最大是多少?

abc137_a +-x

https://atcoder.jp/contests/abc137/tasks/abc137_a
输入A和B
输出A+B A-B A*B三个数字中较大的

abc137_b One Clue

https://atcoder.jp/contests/abc137/tasks/abc137_b
输入k和x
每个整数位置有一个人,已知第x个人确诊了,位置差<k的为密切接触者,问确诊和密切接触者有哪些?
输入x-k+1x+k-12*k-1个数字

abc137_c Green Bin

https://atcoder.jp/contests/abc137/tasks/abc137_c
如果字符串 A和B 出现的字母一样(包括次数),但是顺序不一样,那么他们是一对 anagram
输入 n 个字符串,问其中有多少对 anagram

abc136_a Transfer

https://atcoder.jp/contests/abc136/tasks/abc136_a

桌子上有 2 个杯子,1号杯子最多可以放 A 升水,现在里面包含 B 升水。
2 号杯子包含 C 升水,现在我们将 2 号杯子尽可能地往 1 号杯子里倒,不溢出,
问最后 2 号杯子里还有多少水。

参考代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a,b,c;
    cin>>a>>b>>c;
    cout<<max(c-a+b,0)<<endl;
    return 0;
}

abc136_b Uneven Numbers

https://atcoder.jp/contests/abc136/tasks/abc136_b
输入 n,问 1 到 n 中 有多少个数字有奇数位?

abc136_c Build Stairs

https://atcoder.jp/contests/abc136/tasks/abc136_c
输入 n 个数字,每个数字可以选择不变,或者减少1
问能否让这个序列变成不下降的(相等或上升)?

abc135_a Harmony

https://atcoder.jp/contests/abc135/tasks/abc135_a
输入A和B,找到K使得|A-K|==|B-K|
输出K,如果不存在输入IMPOSSIBLE

abc135_b 0 or 1 Swap

https://atcoder.jp/contests/abc135/tasks/abc135_b
输入一个1到n的排列,问能不能通过交换至多一次让排列变成升序?

abc134_a Dodecagon

https://atcoder.jp/contests/abc134/tasks/abc134_a
输入r,输出3*r*r

abc134_b Golden Apple

https://atcoder.jp/contests/abc134/tasks/abc134_b
一个人如果站在i,可以监视i-d到i+d的所有人,现在1到n共n个人排成一行
问需要几个人才能监视所有人?

abc134_c Exception Handling

https://atcoder.jp/contests/abc134/tasks/abc134_c
输入一个数组,对于每个数字回答,删去这个数字之后数组的最大值是多少?

abc134_e Sequence Decomposing

https://atcoder.jp/contests/abc134/tasks/abc134_e
输入一个序列,分成若干个上升序列,问至少需要几个上升序列?

答案是最长不上升子序列的长度

abc133_a T or T

https://atcoder.jp/contests/abc133/tasks/abc133_a
n 个人出游,如果坐火车,每人花 a 的钱,如果坐汽车,一共花 b 的钱,问最小花费是多少

参考代码:

#include <bits/stdc++.h>
using namespace std; 
int main() {
    int n, a, b;
    cin >> n >> a >> b;
    cout << min(n * a, b) << endl;
    return 0;
}

min 可以计算两个值的中的较小值是多少

abc133_b Good Distance

https://atcoder.jp/contests/abc133/tasks/abc133_b
输入D维空间中的N个点,问其中有多少对点之间的欧几里得距离是整数

abc133_c Remainder Minimization 2019

https://atcoder.jp/contests/abc133/tasks/abc133_c
输入LR,选两个整数ij满足L<=i<j<=R
i*j%2019最小是多少?

abc132_a Fifty-Fifty

https://atcoder.jp/contests/abc132/tasks/abc132_a
输入长度为4的字符串,问这个字符串是不是恰好包含两种字符,每种字符各两个

abc132_b Ordinary Number

https://atcoder.jp/contests/abc132/tasks/abc132_b
输入n个数字,问有多少个p[i]p[i-1],p[i],p[i+1]第二小的

abc132_c Divide the Problems

https://atcoder.jp/contests/abc132/tasks/abc132_c
输入N个数字,保证N是偶数
选一个K,要求大于K的数字恰好有N/2个(当然小于等于K的数字也有N/2个)
K必须是整数,问有多少种选法

abc131_a Security

https://atcoder.jp/contests/abc131/tasks/abc131_a
输入长度为4的数字串,如果有两位相邻的数字相同,输出Bad,否则输出Good

abc131_b Bite Eating

https://atcoder.jp/contests/abc131/tasks/abc131_b
L,L+1,...,L+N-1N个数字,从中删去一个
使得删去之后的和与删去之前的和尽量接近,问删那个?

abc131_c Anti-Division

https://atcoder.jp/contests/abc131/tasks/abc131_c
输入A,B,C,D
问A到B之间,有多少个数字既不是C的倍数,也不是D的倍数

abc130_a Rounding

https://atcoder.jp/contests/abc130/tasks/abc130_a
输入X和A,如果X小于A,输出0,如果X大于等于A,输出10

abc130_b Bounding

https://atcoder.jp/contests/abc130/tasks/abc130_b
输入X和n个长度Li,初始在0,第i次向前走Li的长度,问有几次所在的位置<=X
在0不走也算一次

abc129_a Airplane

https://atcoder.jp/contests/abc129/tasks/abc129_a
有三个地方A,B,C
A和B之间的花费是P
B和C之间的花费是Q
C和A之间的花费是R
从其中任意一个地方出发,到任意一个地方结束,问最小花费是多少?

abc129_b Balance

https://atcoder.jp/contests/abc129/tasks/abc129_b
输入N和一个数组Wi
把N分成前后两部分,使得前半部分之和与后半部分之和的差的绝对值最小
问差的绝对值最小是多少

abc128_a Apple Pie

https://atcoder.jp/contests/abc128/tasks/abc128_a
有A个苹果,P个苹果块,一个苹果可以切成三个苹果块
两个苹果块可以做一个苹果派,问最多做多少个苹果派

abc128_b Guidebook

https://atcoder.jp/contests/abc128/tasks/abc128_b
N个商店,每个商店两个属性,城市和分数
把所有商店排序,先按城市名字典,城市名

abc127_a Ferris Wheel

https://atcoder.jp/contests/abc127/tasks/abc127_a
票价是 A 元( A 是偶数),年龄是 B 岁, B 是整数。
如果年龄大于等于 13 ,要买全票。
如果年龄大于等于 6 且小于等于 12 ,要买半票。
如果年龄小于等于 5 ,不需要买票。
输入 A 和 B ,输出最终的票价。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int A,B;
    cin>>A>>B;
    cout<<(A<13?(A<6?0:B/2):B);
    return 0;
}

a?b:c 表示如果条件a成立,那么结果是b,如果条件a不成立,那么结果是c。
可以在某些情况替代 if 语句

abc127_b Algae

https://atcoder.jp/contests/abc127/tasks/abc127_b
输入rDX[2000]
X[i+1]=r*X[i]-D
输出X[2001], ..., X[2010]

abc127_c Prison

https://atcoder.jp/contests/abc127/tasks/abc127_c
输入M个区间,问交集多大?(交集有多少个整数)

abc126_a Changing a Character

https://atcoder.jp/contests/abc126/tasks/abc126_a
输入一个全大写的字符串,把第K个字符改成小写,输出

abc126_b YYMM or MMYY

https://atcoder.jp/contests/abc126/tasks/abc126_b
输入4位数字,问是否可能 YYMM, MMYY, AMBIGUOUS or NA

abc126_c Dice and Coin

https://atcoder.jp/contests/abc126/tasks/abc126_c
随机一个1n,如果结果>=k那么直接获胜
如果结果<k那么继续扔任意次硬币
如果是正面,结果翻倍
如果是正面,结果归零
问获胜的概率是多少?

abc125_a Biscuit Generator

https://atcoder.jp/contests/abc125/tasks/abc125_a
A秒可以制造B个饼干,T秒可以制造多少饼干?

abc125_b Resale

https://atcoder.jp/contests/abc125/tasks/abc125_b
n个宝石,每个宝石买价C[i],卖价V[i]
选一些宝石买了再卖,问最多赚多少钱

abc125_c GCD on Blackboard

https://atcoder.jp/contests/abc125/tasks/abc125_c
输入 n 个数字,删掉其中一个数字,剩下的数字gcd最大是多少

abc124_a Buttons

https://atcoder.jp/contests/abc124/tasks/abc124_a
按一个写着X的按钮,会获得X的收益,并且按钮上的X会减少1
有两个按钮,一个写着A,一个写着B,按两次
问最大收益是多少?

abc124_b Great Ocean View

https://atcoder.jp/contests/abc124/tasks/abc124_b
输入一个数组,问有多少个数组大于等于自己左边的所有数字

abc123_a Five Antennas

https://atcoder.jp/contests/abc123/tasks/abc123_a
输入a, b, c, d, e和k
如果a, b, c, d, e五个数字两两之差(一共10个差)都<=k,输出Yay!,否则输出:(
输入的a, b, c, d, e是有序的

abc123_b Five Dishes

https://atcoder.jp/contests/abc123/tasks/abc123_b
5个任务,分别需要花A,B,C,D,E的时间完成
但是每个任务开始的时刻必须是10的倍数,问完成这些任务至少需要多久?

abc122_a Double Helix

https://atcoder.jp/contests/abc122/tasks/abc122_a
A和T配对,C和G配对
输入一个字母,输出和他配对的字母

abc122_b ATCoder

https://atcoder.jp/contests/abc122/tasks/abc122_b
输入字符串S,输出字符串S中只包含ACGT四种字母最长的子串

abc122_c GeT AC

https://atcoder.jp/contests/abc122/tasks/abc122_c
输入一个长度为N的字符串,有M个询问
每次询问一个区间,问区间内有多少个子串是AC

abc121_a White Cells

https://atcoder.jp/contests/abc121/tasks/abc121_a
H行W列的一个网格,其中h行w列被染色,问多少个格子没有被染色
注意没有被染色的格子数目,和选哪些行,哪些列染色无关

abc121_b Can you solve this?

https://atcoder.jp/contests/abc121/tasks/abc121_b
输入N,M,C
输入一个长度为M的数组B
输入N个长度为M的数组Ai
问这N个长度为M的数组中有多少个满足Ai1*B1+Ai2*B2+...+Ai*BM+C>0

abc120_a Favorite Sound

https://atcoder.jp/contests/abc120/tasks/abc120_a
买饮料,每个A元,他有B元,他会一直买,直到钱不够,或者已经买C个了
问会买几个

abc120_b K-th Common Divisor

https://atcoder.jp/contests/abc120/tasks/abc120_b
输入A, B, K输出ABK大的公约数,保证存在

abc120_c Unification

https://atcoder.jp/contests/abc120/tasks/abc120_c
输入一个01串,如果0和1相邻,那么会消去(同时左右连接起来)
就这样不断的消去,问最后一共会消去多少字符?

abc120_d Decayed Bridges

https://atcoder.jp/contests/abc120/tasks/abc120_d
n个点,m条边,边一条一条被删掉
问删掉每条边之后有多少对点之间不再互相连通了。

abc119_a Still TBD

https://atcoder.jp/contests/abc119/tasks/abc119_a
输入yyyy/mm/dd的日期,如果小于等于<=2019/04/30输出Heisei,否则输出TBD

abc119_b Digital Gifts

https://atcoder.jp/contests/abc119/tasks/abc119_b
输入n笔收入,1BTC=380000JPY
问总收入是多少JPY

abc118_a B +/- A

https://atcoder.jp/contests/abc118/tasks/abc118_a
输入A和B
如果A是B的约数,输出A+B
否则输出B-A

abc118_b Foods Loved by Everyone

https://atcoder.jp/contests/abc118/tasks/abc118_b
输入N个集合
问所有集合的交集多大?

abc117_a Entrance Examination

https://atcoder.jp/contests/abc117/tasks/abc117_a
输入T和X,输出T/X,保留至少3位小数

abc117_b Polygon

https://atcoder.jp/contests/abc117/tasks/abc117_b
输入n边形的边长,问这样的多边形是否存在?
判断最长边是否小于周长一半,输出Yes或No

abc116_a Right Triangle

https://atcoder.jp/contests/abc116/tasks/abc116_a
输入直角三角形的三条边,求面积。
保证先输入两条直角边,再输入斜边。

#include <bits/stdc++.h>
int main()
{
    int a,b;
    cin>>a>>b;
    cout<<a*b/2<<endl;
}

abc116_b Collatz Problem

https://atcoder.jp/contests/abc116/tasks/abc116_b
从s开始
如果s是偶数,就s=s/2
如果s是奇数,就s=s*3+1
问第一次出现重复的数字是第几个(初始的s是第一个

abc116_c Grand Garden

https://atcoder.jp/contests/abc116/tasks/abc116_c
输入一个数组,每次选一个区间减一,问至少操作多少次可以让数组都变为0。

和NOIP题一模一样
https://www.luogu.com.cn/problem/P1969
https://www.luogu.com.cn/problem/P5019

abc115_a Christmas Eve Eve Eve

https://atcoder.jp/contests/abc115/tasks/abc115_a
输出Christmas后面加(25-N)Eve

abc115_b Christmas Eve Eve

https://atcoder.jp/contests/abc115/tasks/abc115_b
输入n个价格,最贵的半价,其他原价,问总价格是多少?

abc114_a 753

https://atcoder.jp/contests/abc114/tasks/abc114_a
输入一位数,问是不是7,5,3
输出YESNO

abc114_b 754

https://atcoder.jp/contests/abc114/tasks/abc114_b
输入一个数字串,从中取连续三个数字,问和754最小的差是多少

abc114_c 755

https://atcoder.jp/contests/abc114/tasks/abc114_c
问1到N有多少个数字包含3,5,7各至少一次,且不包含其他数字

abc114_c 756

https://atcoder.jp/contests/abc114/tasks/abc114_c
输入N,问N有多少个约数满足:恰好有75个约数?
比如 32400 恰好有 75 个约数

abc113_a Discount Fare

https://atcoder.jp/contests/abc113/tasks/abc113_a
输入X和Y,输出X+Y/2

abc113_b Palace

https://atcoder.jp/contests/abc113/tasks/abc113_b
在高度为x的地方温度为T-x*0.0006
输入n和T和n个位置的高度,问其中温度最接近A的是第几个位置?

abc112_a Programming Education

https://atcoder.jp/contests/abc112/tasks/abc112_a
两种输入,输入1,输出Hello World
输入2,再输入两个整数A和B,输出A+B

abc112_b Time Limit Exceeded

https://atcoder.jp/contests/abc112/tasks/abc112_b
输入N对ci和ti,和时间限制T
问满足ti<=T最大的ci是什么

abc111_a AtCoder Beginner Contest 999

https://atcoder.jp/contests/abc111/tasks/abc111_a
输入一个三位数,只包含9和1
把9改成1,把1改成9,输出

abc111_b AtCoder Beginner Contest 111

https://atcoder.jp/contests/abc111/tasks/abc111_b
输入N,输出大于等于N,最小的111的倍数

abc110_a Maximize the Formula

https://atcoder.jp/contests/abc110/tasks/abc110_a
输入A,B,C三个一位数
输出A+BCAB+C的较大值,
BCAB表示两个数字连起来,不是乘起来

abc110_b 1 Dimensional World's Tale

https://atcoder.jp/contests/abc110/tasks/abc110_b
输入N,M,X,Y
输入长度为N的数组x
输入长度为M的数组y
问是否存在Z满足X<Z<=YZ>数组x中的最大值且Z<=数组y中的最小值

abc110_d Factorization

https://atcoder.jp/contests/abc110/tasks/abc110_d
输入N和M
把M分成N个数字的乘积,问方案数

隔板法

abc109_a ABC333

https://atcoder.jp/contests/abc109/tasks/abc109_a
输入两个数字A和B,问乘积是否是奇数,输出YesNo

abc109_b Shiritori

https://atcoder.jp/contests/abc109/tasks/abc109_b
输入nn个字符串,问有没有重复的
如果有重复的,输出No,否则输出Yes

abc109_c Skip

https://atcoder.jp/contests/abc109/tasks/abc109_c
数轴上有n个点,第i个点的坐标是xi
我们的起点是X,目标是访问所有点
出发之前我们要选一个整数D,之后每次移动,只能是加D或者减D
问D最大是多大?

abc108_a Pair

https://atcoder.jp/contests/abc108/tasks/abc108_a
输入K,问从1到K中选一个奇数和一个偶数有多少种方案?

abc108_b Ruined Square

https://atcoder.jp/contests/abc108/tasks/abc108_b
已知(x1,y1),(x2,y2),(x3,y3),(x4,y4)是一个正方形的四个顶点,按逆时针顺序
已知(x1,y1),(x2,y2)的坐标
(x3,y3),(x4,y4)的坐标

abc107_a Train

https://atcoder.jp/contests/abc107/tasks/abc107_a
一共N节车厢,问正数第i节车厢是倒数第几节车厢?

abc107_b Grid Compression

https://atcoder.jp/contests/abc107/tasks/abc107_b
输入HW列,包含.#的字符矩阵
删去全是.的行和列,输出剩下的字符矩阵

abc106_a Garden

https://atcoder.jp/contests/abc106/tasks/abc106_a
A行B列的网格,删掉一行一列,问剩下多少个格子?

abc106_b 105

https://atcoder.jp/contests/abc106/tasks/abc106_b
问1到n中有几个奇数恰好有8个约数

abc106_c To Infinity

https://atcoder.jp/contests/abc106/tasks/abc106_c
输入一个数字串S,只包含19
一次操作会把数字x替换为x个x,比如1214会变成12214444
对数字串S做5e15次操作,问第K个字符是什么?

相当于第一个非1的数字重复无限次,问第K个字符是什么
相当于只需要第一个非1的数字是什么,下标是什么

abc105_a AtCoder Crackers

https://atcoder.jp/contests/abc105/tasks/abc105_a
N个东西分给K个人,希望拿最多的人和拿最少的人之间的差越少越好,问最少是多少?

abc105_b Cakes and Donuts

https://atcoder.jp/contests/abc105/tasks/abc105_b
蛋糕4元一个,甜甜圈7元一个
输入N元,要求买至少两个蛋糕,至少两个甜甜圈,问能否恰好花N元

abc104_a Rated for Me

https://atcoder.jp/contests/abc104/tasks/abc104_a
输入一个rating值R
如果R<1200,那么输出ABC
如果1200<=R<2800,那么输出ARC
如果2800<=R,那么输出AGC

abc104_b AcCepted

https://atcoder.jp/contests/abc104/tasks/abc104_b
输入字符串S,问是否满足以下所有条件,输出Yes或No
第一个字母是大写A
从第三个字符到倒数第二个字符,恰好出现一个大写C
除了上面提到的A和C,所有字母都是小写

abc103_a Task Scheduling Problem

https://atcoder.jp/contests/abc103/tasks/abc103_a
一共3个任务
完成第一个任务0的代价
如果上一个完成的是ai,之后完成aj的代价是abs(ai-aj)
问如何完成三个任务代价最小

abc103_b String Rotation

https://atcoder.jp/contests/abc103/tasks/abc103_b
输入两个字符串S和T,问能否将S循环移位后让S和T相等
循环移位是指把最后若干个字符移到字符串开始

abc102_a Multiple of 2 and N

https://atcoder.jp/contests/abc102/tasks/abc102_a
输入n,输出 lcm(2, n)
lcm表示最小公倍数

abc102_b Maximum Difference

https://atcoder.jp/contests/abc102/tasks/abc102_b
输入数组A,从中选两个数字,差值越大越好
问最大的差值是多少?

abc101_a Eating Symbols Easy

https://atcoder.jp/contests/abc101/tasks/abc101_a
输入一个长度为4,由+-组成的字符串
输入(+的个数) - (-的个数)

abc101_b Digit Sums

https://atcoder.jp/contests/abc101/tasks/abc101_b
设 S(n) 表示 n 各个位之和,比如 S(101) = 2
输入 n 问 S(n) 是不是 n 的约数,输出Yes或No

abc100_a Happy Birthday!

https://atcoder.jp/contests/abc100/tasks/abc100_a
输入两个数字,如果都<=8,那么输出Yay!,否则输出:(

abc100_b Ringo's Favorite Numbers

https://atcoder.jp/contests/abc100/tasks/abc100_b
输入D和N
输出第N
是100的D次方的倍数

不是100的D+1次方的倍数
的数字

abc099_a ABD

https://atcoder.jp/contests/abc099/tasks/abc099_a
输入一个数字N,如果小于1000,输出ABC否则输出ABD

abc099_b Stone Monument

https://atcoder.jp/contests/abc099/tasks/abc099_b
输入ab,求x,使得a+xb+x是相邻的三角数,保证有解
三角数是指可以表示为n*(n+1)/2的数字

abc099_c Strange Bank

https://atcoder.jp/contests/abc099/tasks/abc099_c
用6的次幂和9的次幂凑出N,问至少需要几个数字?

abc098_a Add Sub Mul

https://atcoder.jp/contests/abc098/tasks/abc098_a
输入A和B
输出A+B A-B A*B三个数字中较大的

abc098_b Cut and Count

https://atcoder.jp/contests/abc098/tasks/abc098_b
输入一个字符串,分成前后两端,希望让同时出现在前后的字母种类最多,问最多多少种?

abc097_a Colorful Transceivers

https://atcoder.jp/contests/abc097/tasks/abc097_a
输入数轴上三个人的位置a, b, c
距离小于等于d的两个人连通,问三个人是否连通

abc097_b Exponential

https://atcoder.jp/contests/abc097/tasks/abc097_b
输入X,输出小于等于X,最大的次幂
次幂是可以表示为正整数A的正整数B次方,B大于等于2

abc096_a Day of Takahashi

https://atcoder.jp/contests/abc096/tasks/abc096_a
输入a和b,问从1月1日到a月b日,有多少天,月份和日期相同?

abc096_b Maximum Sum

https://atcoder.jp/contests/abc096/tasks/abc096_b
输入A,B,C三个数字,可以执行K次操作,每次选一个数字乘以2,问最后最大的和是多少?

abc096_c Grid Repainting 2

https://atcoder.jp/contests/abc096/tasks/abc096_c
输入一个图案,#表示涂黑,.表示空白,问能不能用1*2的画笔画出

如果有孤立的1*1#就不行

abc096_d Five, Five Everywhere

https://atcoder.jp/contests/abc096/tasks/abc096_d
输入n个质数,使得任意5个质数的和都是合数

输入前n个质数,包括2

abc095_a Something on It

https://atcoder.jp/contests/abc095/tasks/abc095_a
输入一个长度为3的字符串
输出700+(o的个数)*100

abc095_b Bitter Alchemy

https://atcoder.jp/contests/abc095/tasks/abc095_b
输入N种点心,X克原料,第i种点心需要mi的原料
要求每种点心至少做一个,问至多做多少个点心

abc094_a Cats and Dogs

https://atcoder.jp/contests/abc094/tasks/abc094_a
A+B个动物,其中A个一定是猫,B个可能是猫可能是狗
问是否可能有恰好X只猫,输出YESNO

abc094_b Toll Gates

https://atcoder.jp/contests/abc094/tasks/abc094_b
输入N,M,X和M个正整数Ai
一共N+1个格子,标号从0到N
从标号为X的格子出发,每次可以向前或向后
其中M个格子收费,标号为Ai,保证0,X,N不收费
问从X出发走到0或N最小代价是多少?

abc093_a abc of ABC

https://atcoder.jp/contests/abc093/tasks/abc093_a
输入长度为3的字符串,问重排之后能不能得到abc
输出YesNo

abc093_b Small and Large Integers

https://atcoder.jp/contests/abc093/tasks/abc093_b
输入区间A,B和数字K
输出A和B之间所有是最小K个,或最大K个的数字

abc092_a Traveling Budget

https://atcoder.jp/contests/abc092/tasks/abc092_a
做一件事分两步
做第一步有两种方法,代价分别是AB
做第二步有两种方法,代价分别是CD
问做这件事最小代价是多少?min(A,B)+min(C,D)

abc092_b Chocolate

https://atcoder.jp/contests/abc092/tasks/abc092_b
输入N,D,X和N个数字Ai
一共D天,标号从1到D
第i个人会在第1,第Ai+1,第2*Ai+1天,一直到结束,中的每天吃掉一个巧克力
最后会剩下X个巧克力
问开始需要准备多少个巧克力

abc091_a Two Coins

https://atcoder.jp/contests/abc091/tasks/abc091_a
有两个硬币,一个面值是A元,一个面值是B元,问能否买价格为C元的的东西
输出YesNo

abc091_b Two Colors Card Game

https://atcoder.jp/contests/abc091/tasks/abc091_b
输入n个字符串数组s和m个字符串数组t
找到一个字符串使得s中出现的-t中出现的次数最大。
只需要输出这个最大的差值。

abc090_a Diagonal String

https://atcoder.jp/contests/abc090/tasks/abc090_a
输入一个3x3的字符矩阵
输出左上到右下的三个字符

abc090_b Palindromic Numbers

https://atcoder.jp/contests/abc090/tasks/abc090_b
输入A和B,问A到B之间多少个数字是回文数?
保证A和B都是5位数

abc089_a Grouping 2

https://atcoder.jp/contests/abc089/tasks/abc089_a
N个学生分成若干组,使得至少三人的组数最多,问最多可以多少组

abc089_b Hina Arare

https://atcoder.jp/contests/abc089/tasks/abc089_b
输入N和N个字符,问出现了几种字符
保证只会有PWGY四种字符,且PWG一定出现(答案是3或4)

abc088_a Infinite Coins

https://atcoder.jp/contests/abc088/tasks/abc088_a
有A个1元硬币,无限多个500元硬币,问能否恰好支付N元?

abc088_b Card Game for Two

https://atcoder.jp/contests/abc088/tasks/abc088_b
输入长度为N的数组,双方轮流取,每次取剩下的数字中最大的
问 先手取到的数字之和 减去 后手取到的数字之和 是多少?

abc087_a Buying Sweets

https://atcoder.jp/contests/abc087/tasks/abc087_a
初始有X元,先买一个A元的东西,再尽可能多的买B元的东西
问最后剩下多少钱?

abc087_b Coins

https://atcoder.jp/contests/abc087/tasks/abc087_b
A个500元,B个100元,C个50元,问凑出X元有多少种方法?

abc086_a Product

https://atcoder.jp/contests/abc086/tasks/abc086_a
输入两个整数a和b,问a*b是奇数还是偶数,输出Odd或Even

abc086_b 1 21

https://atcoder.jp/contests/abc086/tasks/abc086_b
输入两个整数a和b,问a和b连接起来是不是完全平方数?

abc085_a Already 2018

https://atcoder.jp/contests/abc085/tasks/abc085_a
输入一个日期yyyy/mm/dd保证是2017年1月的日期,输出2018/01/dd

abc085_b Kagami Mochi

https://atcoder.jp/contests/abc085/tasks/abc085_b
输入N个数字,问其中有几种不同的数字

abc085_c Otoshidama

https://atcoder.jp/contests/abc085/tasks/abc085_c
有1000,5000,10000三种面值的纸币
问N张纸币的总面值,是否可能是Y,输出YesNo

abc084_a New Year

https://atcoder.jp/contests/abc084/tasks/abc084_a
现在是12月30日的M点整,问还有多少小时元旦?

abc084_b Postal Code

https://atcoder.jp/contests/abc084/tasks/abc084_b
输入两个数字A和B和长度为A+B+1的字符串S
问S是不是由A个数字加一个减号-加B个数字组成的,输出Yes或No

abc083_a Libra

https://atcoder.jp/contests/abc083/tasks/abc083_a
天平左边放A和B,右边放C和D,问哪边重?

abc083_b Some Sums

https://atcoder.jp/contests/abc083/tasks/abc083_b
输入N,A,B
1N之间有多少个数字各个位之和在AB之间

abc082_a Round Up the Mean

https://atcoder.jp/contests/abc082/tasks/abc082_a
输入两个整数,输出他们的平均值,向上取整

abc082_b Two Anagrams

输入两个字符串st,可以重排这两个字符串得到s't'
问有没有可能s'<t',输出YesNo

abc081_a Placing Marbles

https://atcoder.jp/contests/abc081/tasks/abc081_a
输入长度为3的字符串,问其中有多少个字符1

abc081_b Shift only

https://atcoder.jp/contests/abc081/tasks/abc081_b
输入一个数组,如果所有数字都是偶数,每次操作可以让所有数字都除以2
问至多执行几次操作?

abc080_a Parking

https://atcoder.jp/contests/abc080/tasks/abc080_a
输入N A B输出N*AB的较小值

abc080_b Harshad Number

https://atcoder.jp/contests/abc080/tasks/abc080_b
输入N,设f(N)N各个位之和
N是否是f(N)的倍数,输出YesNo

abc079_a Good Integer

https://atcoder.jp/contests/abc079/tasks/abc079_a
输入一个长度为4的字符串,如果其中有连续三位一样,那么输出Yes,否则输出No

abc079_b Lucas Number

https://atcoder.jp/contests/abc079/tasks/abc079_b
L[0]=2, L[1]=1, L[i]=L[i-1]+L[i-2]
输入N,输出L[N]

abc078_a HEX

https://atcoder.jp/contests/abc078/tasks/abc078_a
输入两个字符,是ABCEDF中的一个,问是<=>三个关系中的哪个关系?

abc078_b ISU

https://atcoder.jp/contests/abc078/tasks/abc078_b
一个长度为X厘米的椅子,每个人坐下需要Y厘米的空间,相邻两个人,人和椅子的端点间隔至少Z厘米。

abc077_a Rotation

https://atcoder.jp/contests/abc077/tasks/abc077_a
输入2行3列的字符矩阵,每个字符表示一种颜色,问旋转180度之后和自己是否一样

abc077_b Around Square

https://atcoder.jp/contests/abc077/tasks/abc077_b
输入N,输出小于等于N最大的完全平方数。

abc076_a Rating Goal

https://atcoder.jp/contests/abc076/tasks/abc076_a
输入R和G,问什么数字和R的平均值是G?

abc076_b Addition and Multiplication

https://atcoder.jp/contests/abc076/tasks/abc076_b
初始是1,每次操作可以+K或者*2,问N次操作之后最小是多少?

abc075_a One out of Three

https://atcoder.jp/contests/abc075/tasks/abc075_a
输入三个数字A, B, C,其中两个相同,第三个不同
输出不同的那个数字

abc075_b Minesweeper

https://atcoder.jp/contests/abc075/tasks/abc075_b
生成扫雷地图
输入一个H行W列的字符数组,#表示雷,.表示空地
把空地替换成周围雷的个数

abc074_a Bichrome Cells

https://atcoder.jp/contests/abc074/tasks/abc074_a
输入N和A,N乘以N的网格,其中A个格子被染色了,问没有被染色的格子有多少个?

abc074_b Collecting Balls (Easy Version)

https://atcoder.jp/contests/abc074/tasks/abc074_b
N个球,长度为K的区间,两个机器人在0和K的位置上,中间有N个球,位置是Xi
机器人捡球,同时只能捡一个球,问机器人移动距离最小是多少?

abc073_a September 9

https://atcoder.jp/contests/abc073/tasks/abc073_a
输入一个二位数,问是否包含9

abc073_b Trapezoid Sum

https://atcoder.jp/contests/abc073/tasks/abc073_b
输入N个互不相交的区间,第i个区间是li到ri,包含两个端点
问所有区间内有多少个整数

abc073_b Theater

https://atcoder.jp/contests/abc073/tasks/abc073_b
输入N个区间,区间之间不重叠,问总共包含多少个数字
区间两端都算包含,比如24到30的区间一共是7个数字

abc073_c Write and Erase

https://atcoder.jp/contests/abc073/tasks/abc073_c
输入N个数字,问其中有几种数字出现了奇数次?
map练习题

abc072_a Sandglass2

https://atcoder.jp/contests/abc073/tasks/abc072_a
输入X和t,一个沙漏,初始X的沙子,每秒漏1的沙子,问t秒后剩多少沙子

abc071_b Not Found

https://atcoder.jp/contests/abc071/tasks/abc071_b
输入一个字符串,问字典序最小没有出现过的字母是什么?
如果a到z都出现过,输出None

abc071_a Meal Delivery

https://atcoder.jp/contests/abc071/tasks/abc071_a
输入x, a, b问x离a近还是离b近,输出AB,保证不一样近

abc070_a Palindromic Number

https://atcoder.jp/contests/abc070/tasks/abc070_a
输入三位数N,问是不是回文数?

abc070_b Two Switche

https://atcoder.jp/contests/abc070/tasks/abc070_b
甲在时刻A秒按下按钮,时刻B秒松开按钮
乙在时刻C秒按下按钮,时刻D秒松开按钮
问甲乙同时按下按钮多少秒?

abc069_a K-City

https://atcoder.jp/contests/abc069/tasks/abc069_a
画n条横线,m条竖线,问有多少个方格?(参考样例)

abc069_b i18n

https://atcoder.jp/contests/abc069/tasks/abc069_b
输入一个字符串,输出第一个字母,字符串长度减2,最后一个字母

abc068_a ABCxxx

https://atcoder.jp/contests/abc068/tasks/abc068_a
输入一个三位数N,输出三个字母ABC和数字N,比如输入123,输出ABC123

abc068_b Break Number

https://atcoder.jp/contests/abc068/tasks/abc068_b
输入N,问小于等于N,最大的2的次幂是多少?

abc067_a Sharing Cookies

https://atcoder.jp/contests/abc067/tasks/abc067_a
输入A和B,如果A,B,A+B三个数字中有3的倍数,输出Possible,否则输出Impossible

abc067_b Snake Toy

https://atcoder.jp/contests/abc067/tasks/abc067_b
输入n个数,输出前k大的数的总和

abc066_a ringring

https://atcoder.jp/contests/abc066/tasks/abc066_a
有三个车铃,价格分别是 a, b, c 买其中便宜的两个需要多少钱

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a, b, c; cin >> a >> b >> c;
    cout << min(a + b, min(b + c, c + a));
}

abc066_b ss

https://atcoder.jp/contests/abc066/tasks/abc066_b
输入一个字符串,在结尾删掉尽量少的字符,要求留下的字符串长度为偶数并且前一半和后一半相同
问留下的字符串最长多长?

abc065_a Expired?

https://atcoder.jp/contests/abc065/tasks/abc065_a
买了一个保质期A天的食物,B天后吃掉它,问会怎样
如果在保质期之前包括当天(B<=A)吃掉,输出delicious
如果过期不到X天(B<=A+X)吃掉,输出safe
如果过期超过X天(B>A+X)吃掉,输出dangerous

abc065_b Trained?

https://atcoder.jp/contests/abc065/tasks/abc065_b
有N个按钮,任何时候都只有一个按钮会亮
如果按钮i是亮的,按一下按钮i,按钮i会灭,然后按钮ai变亮
按一个不亮的按钮,没有任何效果
初始按钮1是亮的,目标让按钮2变亮
问至少操作几次

abc064_a RGB Cards

https://atcoder.jp/contests/abc064/tasks/abc064_a
输入r g b三个一位数,问把这三个连成一个三位数,是否是4的倍数,输出YESNO

abc064_b Traveling AtCoDeer Problem

https://atcoder.jp/contests/abc234/tasks/abc064_b
输入N个点,从中选两个点,选出的两个点之间距离最大是多少?

abc063_a Restricted

https://atcoder.jp/contests/abc063/tasks/abc063_a
输入两个数字,输出他们的和,但如果和大于等于10,输出error

abc063_b Varied

https://atcoder.jp/contests/abc063/tasks/abc063_b
输入字符串S,问S中字母是不是互不相同的
输出yes或no

abc062_a Grouping

https://atcoder.jp/contests/abc062/tasks/abc062_a
输入x和y,问这两个月份的天数是否一样

abc062_b Picture Frame

https://atcoder.jp/contests/abc062/tasks/abc062_b
输入H行W列的字符数组,输出外面包一圈#的结果

abc061_a Between Two Integers

https://atcoder.jp/contests/abc061/tasks/abc061_a
输入三个数字A, B, C
问C是否在A和B之间

abc061_b Counting Roads

https://atcoder.jp/contests/abc061/tasks/abc061_b
输入一个图,问每个点的度数是多少
第一行N个点,M条边
接下来M行,每行两个数字,表示一条边

abc060_a Shiritori

https://atcoder.jp/contests/abc060/tasks/abc060_a
输入三个字符串A, B, C
问是否A的最后一个字母等于B的第一个字母且B的最后一个字母等于C第一个字母
输出YES或NO

abc060_b Choose Integers

https://atcoder.jp/contests/abc060/tasks/abc060_b
输入A,B,C
问是否存在A的倍数,模B,余数是C,输出YES或NO

abc059_a Three-letter acronym

https://atcoder.jp/contests/abc059/tasks/abc059_a
输入三个单词,输出三个单词的首字母大写

abc059_b Comparison

https://atcoder.jp/contests/abc059/tasks/abc059_b
输入两个数字AB
如果A大于B,输出GREATER
如果A等于B,输出EQUAL
如果A小于B,输出LESS
注意AB的范围!

abc058_a ι⊥l

https://atcoder.jp/contests/abc058/tasks/abc058_a
输入三个数字a,b,c如果b-a==c-b输出YES否则输出NO

abc058_b ∵∴∵

https://atcoder.jp/contests/abc058/tasks/abc058_b
输入两个字符串O和E
已知O是密码的所有奇数位置(从1开始下标)
已知E是密码的所有偶数位置(从1开始下标)
复原密码

abc057_a Remaining Time

https://atcoder.jp/contests/abc057/tasks/abc057_a
现在时间是A点整,问B小时之后是几点整,输出一个0到23之间的数字

abc057_b Checkpoints

https://atcoder.jp/contests/abc057/tasks/abc057_b
有N个学生,M个检查站,
第i个学生的坐标是(ai, bi)
第i个检查站的坐标是(ci, di)
对于每个学生,输出曼哈顿距离最近的检查站的下标
如果有多个检查站距离相等,输出下标最小的
第i个学生到第j个检查站的曼哈顿距离是abs(a[i]-c[j])+abs(b[i]-d[j])

abc056_a HonestOrDishonest

https://atcoder.jp/contests/abc056/tasks/abc056_a
输入两个字母,相同输出H,不同输出D

abc056_b NarrowRectanglesEasy

https://atcoder.jp/contests/abc056/tasks/abc056_b
输入W,a,b
两个区间[a, a+W],[b, b+W]
要向左或向右移动[b, b+W]使得两个区间有交集,问最少移动多少距离?

abc055_a Restaurant

https://atcoder.jp/contests/abc055/tasks/abc055_a
800日元一顿饭,每吃15顿可以获得200日元的优惠,问吃n顿花多少钱

abc055_b Training Camp

https://atcoder.jp/contests/abc055/tasks/abc055_b
输入N,输出N的阶乘,模1000000007

abc054_a One Card Poker

https://atcoder.jp/contests/abc054/tasks/abc054_a
输入两个扑克牌数字1到13之间,问哪个大?2最小,1最大。
第一个数字大输出Alice
第二个数字大输出Bob
一样大输出Draw

abc054_b Template Matching

https://atcoder.jp/contests/abc054/tasks/abc054_b
输入一个N*N的字符矩阵A,和M*M的字符矩阵B
问A是否包含B,B不能旋转和对称

abc053_a ABC/ARC

https://atcoder.jp/contests/abc053/tasks/abc053_a
输入x,如果x<1200输出ABC,如果x>=1200输出ARC

abc053_b A to Z String

https://atcoder.jp/contests/abc053/tasks/abc053_b
输入一个字符串,找到最长的一个子串,以A开始,以Z结束
保证有解,输出最长的长度。

abc052_a Two Rectangles

https://atcoder.jp/contests/abc052/tasks/abc052_a
输入 A乘B 和 C乘D 的两个矩形,输出其中较大的面积

abc052_b Increment Decrement

https://atcoder.jp/contests/abc052/tasks/abc052_b
一个变量初始为0,输入一个操作序列S,I表示增加一,D表示减少一
问这个变量的到过的最大值是多少?

abc051_a Haiku

https://atcoder.jp/contests/abc051/tasks/abc051_a
输入一个字符串,把第6位和第14位的字符替换成空格

abc051_b Sum of Three Integers

https://atcoder.jp/contests/abc051/tasks/abc051_b
输入KS
问有多少组(X,Y,Z)满足0<=X,Y,Z<=KX+Y+Z==S

abc050_a Addition and Subtraction Easy

https://atcoder.jp/contests/abc050/tasks/abc050_a
输入A op B其中AB是整数,op是加号或减号
输出计算结果

abc050_b Contest with Drinks Easy

https://atcoder.jp/contests/abc050/tasks/abc050_b
输入N个数字Ti
有M个询问,第i个询问是问
如果把第Pi个数字改成Xi,那么这N个数字之和是多少?
(注意并不是真的改)

abc049_a UOIAUAI

https://atcoder.jp/contests/abc049/tasks/abc049_a
输入一个小写字母,问是不是元音

abc049_b Thin

https://atcoder.jp/contests/abc049/tasks/abc049_b
输入一个H行W列的字符矩阵,把每行重复两遍输出

abc048_a AtCoder *** Contest

https://atcoder.jp/contests/abc048/tasks/abc048_a
输入三个字符串,输入三个字符串的第一个字符

abc048_b Between a and b ...

https://atcoder.jp/contests/abc048/tasks/abc048_b
输入a, b, x 问 a 到 b 之间有多少个 x 的倍数?

abc047_a Fighting over Candies

https://atcoder.jp/contests/abc047/tasks/abc047_a
输入三个数字,问其中有没有两个的和是第三个,输出YesNo

abc047_b Snuke's Coloring 2 (ABC Edit)

https://atcoder.jp/contests/abc047/tasks/abc047_b
输入左下角为 (0,0) 右上角为 (W,H) 的矩形,和N个操作
每个操作是xi, yi, ai
如果ai==1,会把 < xi 的区域涂黑
如果ai==2,会把 > xi 的区域涂黑
如果ai==3,会把 < yi 的区域涂黑
如果ai==4,会把 > yi 的区域涂黑
问最后没被涂黑的面积是多少

abc046_a AtCoDeer and Paint Cans

https://atcoder.jp/contests/abc046/tasks/abc046_a
输入三个数字,问有几种不同的数字?

abc046_b Painting Balls with AtCoDeer

https://atcoder.jp/contests/abc046/tasks/abc046_b
N 个球染 K 种颜色,要求相邻的球颜色不同,问方案数?

abc045_a Trapezoids

https://atcoder.jp/contests/abc045/tasks/abc045_a
输入梯形的上底a,下底b,高h
输出面积,保证整数

abc045_b Card Game for Three (ABC Edit)

https://atcoder.jp/contests/abc045/tasks/abc045_b
A B C 三个人打牌,牌有 a b c 三种,必须按顺序从前往后出(不需要决策)
A 先出,上一张出什么字母,下一张就轮到哪个人出牌
如果轮到一个人,这个人无牌可出,那么这个人就赢了
问谁能赢

abc044_a Tak and Hotels (ABC Edit)

https://atcoder.jp/contests/abc044/tasks/abc044_a
要住N晚酒店,每晚X元,如果住够了K晚,之后每晚Y(Y<X)
输入N,K,X,Y问需要花多少元?

abc044_b Beautiful Strings

https://atcoder.jp/contests/abc044/tasks/abc044_b
输入一个字符串,问是不是每个字母都出现了偶数次

abc043_a Children and Candies (ABC Edit)

https://atcoder.jp/contests/abc043/tasks/abc043_a
输入n,问1+2+3+...+n是多少?

abc043_b Unhappy Hacking (ABC Edit)

https://atcoder.jp/contests/abc043/tasks/abc043_b
模拟输入窗口
如果是0,输入0
如果是1,输入1
如果是B,删除最后一个字符,但如果字符串已经被删空了,什么都不会发生
问最终结果是什么

abc042_a Iroha and Haiku

https://atcoder.jp/contests/abc042/tasks/abc042_a
输入三个数字a, b, c
问重排之后能不能得到5, 7, 5
输出YES或NO

abc042_b Iroha Loves Strings (ABC Edition)

https://atcoder.jp/contests/abc042/tasks/abc042_b
输入N个长度为L的字符串,希望连接起来字典序最小,问如何连接?

abc041_a 添字

https://atcoder.jp/contests/abc041/tasks/abc041_a
输入一个字符串s,问第i个字母是什么

abc041_b 直方体

https://atcoder.jp/contests/abc041/tasks/abc041_b

输入正方体的长宽高A,B,C

abc041_d 徒競走

https://atcoder.jp/contests/abc041/tasks/abc041_d
输入一个DAG问有多少种拓扑排序

abc040_a 赤赤赤赤青

https://atcoder.jp/contests/abc040/tasks/abc040_a
一共n个球,第x个球是青色的,其他的球是赤色的
每次操作可以交换两个相邻的球
希望把青色的球换到最左端或最右端
问至少需要几次操作?

abc040_b □□□□□

https://atcoder.jp/contests/abc040/tasks/abc040_b

abc040_c

https://atcoder.jp/contests/abc040/tasks/abc040_c
从1走到n,从i可以走到i+1或i+2,代价是高度之差的绝对值,问最小待机啊

abc039_a 高橋直体

https://atcoder.jp/contests/abc039/tasks/abc039_a
输入立方体的长A,宽B,高C,问表面积是多少?

abc039_b エージェント高橋君

https://atcoder.jp/contests/abc039/tasks/abc039_b
输入N,输出N的四次方根,保证四次方根是整数

abc039_c

https://atcoder.jp/contests/abc039/tasks/abc039_c
钢琴键盘是
WBWBWWBWBWBW WBWBWWBWBWBW WBWBWWBWBWBW ...
无限循环,循环节长度是 12
这12个键对应的音阶是
Do,Do,Re,Re,Mi,Fa,Fa,So,So,La,La, Si

输入当前位置和当前位置右边共20个键而定颜色(B或W)
问当前位置的音节是什么

输入格式 一个字符串,W表示白色,B表示黑色
输出格式 音阶中的一个,高桥君所在的键盘颜色与输入的第一个相对应

abc039_d

https://atcoder.jp/contests/abc039/tasks/abc039_d
输入一个字符矩阵,问是否可能由一个字符矩阵扩散得到
扩散:每个#往8个方向扩散
如果可以,输出possible和方案
如果不可以,输出impossible

abc038_a お茶

https://atcoder.jp/contests/abc038/tasks/abc038_a
输入一个字符串,问是不是以T结尾,输出YESNO

abc038_b ディスプレイ

https://atcoder.jp/contests/abc038/tasks/abc038_b
输入两个矩形的长宽,问能不能把两个矩形按某条边对齐,拼成一个更大的矩形
输出YesNo

abc037_a 饅頭

https://atcoder.jp/contests/abc037/tasks/abc037_a
白馒头A元一个,绿馒头B元一个,初始有C元,问最多买几个馒头?

abc037_b 編集

https://atcoder.jp/contests/abc037/tasks/abc037_b
长度为N的数组,Q次操作,每次操作区间把区间LR都修改成T
问最后数组是多少?

abc036_a お茶

https://atcoder.jp/contests/abc036/tasks/abc036_a
输入A和B,输出B/A上取整

abc036_b 回転

https://atcoder.jp/contests/abc036/tasks/abc036_b
输入N行N列字符矩阵,输出顺时针旋转90度的结果

abc035_a テレビ

https://atcoder.jp/contests/abc035/tasks/abc035_a
输入W和H,问W:H4:3还是16:9

abc035_b ドローン

https://atcoder.jp/contests/abc035/tasks/abc035_b
输入LRDU?序列,把?替换成LRDU之一,按照这个序列走,问离起点最远最近是多少?

abc034_a テスト

https://atcoder.jp/contests/abc034/tasks/abc034_a
输入两个不相等的数字,如果第二个更大,输出Better,如果第一个更大,输出Worse

abc034_b ペア

https://atcoder.jp/contests/abc034/tasks/abc034_b
已知1和2一对,3和4一对,5和6一对……
输入n,问n和谁一对?

abc033_a 暗証番号

https://atcoder.jp/contests/abc033/tasks/abc033_a
输入一个四位数N,如果四位都相同,输出SAME,否则输出DIFFERENT

abc033_b 町の合併

https://atcoder.jp/contests/abc033/tasks/abc033_b
投票取名,输入n个选项对应的人数,问是否有选项大于一半?
如果有,输出对应的字符串,如果没有,输出 atcoder

abc032_a 高橋君と青木君の好きな数

https://atcoder.jp/contests/abc032/tasks/abc032_a
输入a,b,n
问大于等于n的数字中,既是a的倍数,又是b的倍数,最小是多少?

abc032_b 高橋君とパスワード

https://atcoder.jp/contests/abc032/tasks/abc032_b
输入字符串 s 和长度 k
问字符串 s 的长度为 k 的子串中,有多少个不同的字符串
abcabc 的长度为 2 的子串有 5 个{ab, bc, ca, ab, bc} 但不同的只有三个 {ab, bc, ca}

abc031_a ゲーム

https://atcoder.jp/contests/abc031/tasks/abc031_a
输入AD
可以让A自增1或者D自增1
问最大可以让A*D变成多大?

abc031_b 運動管理

https://atcoder.jp/contests/abc031/tasks/abc031_b

abc030_a 勝率計算

https://atcoder.jp/contests/abc030/tasks/abc030_a
输入A,B,C,D
如果B/A<D/C,那么输出AOKI
如果B/A==D/C,那么输出DRAW
如果B/A>D/C,那么输出TAKAHASHI

abc030_b 時計盤

https://atcoder.jp/contests/abc030/tasks/abc030_b
n 时 m 分,时针和分针夹角是多少度?

abc029_a 複数形

https://atcoder.jp/contests/abc029/tasks/abc029_a
输入一个字符串,结尾加s输出

abc029_b カキ

https://atcoder.jp/contests/abc029/tasks/abc029_b
输入12个字符串,问其中有多少个字符串包含字母r

abc028_a テスト評価

https://atcoder.jp/contests/abc028/tasks/abc028_a
输入一个数字N
如果N<=59输出Bad
如果60<=N<=89输出Good
如果90<=N<=99输出Great
如果N==100输出Perfect

abc028_b 文字数カウント

https://atcoder.jp/contests/abc028/tasks/abc028_b
输入一个只包含ABCDEF的字符串
输出ABCDEF六个字符出现的次数

abc027_a 長方形

https://atcoder.jp/contests/abc027/tasks/abc027_a
给定一个矩形三边的长度。 求剩余一边的长度。

abc027_b 島と橋

https://atcoder.jp/contests/abc027/tasks/abc027_b
输入n座岛的人口,目标让所有的岛人口相同,要在相邻的岛之间造桥
问至少造多少座桥?

abc026_a 掛け算の最大値

https://atcoder.jp/contests/abc026/tasks/abc026_a
输入一个正偶数A,把A分成两个正整数的和,让这两个正整数的成绩最大

abc026_b N重丸

https://atcoder.jp/contests/abc026/tasks/abc026_b
输入N个同心圆的半径,从最外层开始,红色和白色交替
问红色的总面积是多少?

abc025_a 25個の文字列

https://atcoder.jp/contests/abc025/tasks/abc025_a
输入5个有序的字母,从中有顺序的选两个(可以重复)可以得到25个单词
问这些单词中第N个是什么

abc025_b 双子とスイカ割り

https://atcoder.jp/contests/abc025/tasks/abc025_b
输入n和上下限A和B
接下来n次移动,先是一个单词West和East表示方向,接下来一个距离di
如果di小于A,那么会被改成A
如果di大于B,那么会被改成B
问最后停留的位置是什么?
如果在起点的East方向,那么输出East和距离
如果在起点的West方向,那么输出West和距离
如果在起点,那么输出 0

abc024_a 動物園

https://atcoder.jp/contests/abc024/tasks/abc024_a
儿童票A元,成人票B元,团体票C元,保证C<=B<=A
团体票必须大于等于K个人才能用
现在有S个儿童,T个成人,问买票需要多少钱?

abc024_b 自動ドア

https://atcoder.jp/contests/abc024/tasks/abc024_b
输入区间个数n,区间长度T,和n个区间的起点,问这些区间的并集多长

abc023_a 加算王

https://atcoder.jp/contests/abc023/tasks/abc023_a
输入一个两位数(10 ~ 99之间),输出个位+十位

abc023_b 手芸王

https://atcoder.jp/contests/abc023/tasks/abc023_b
初始一个空字符串
第0次操作添加一个b
第1,4,7,...次操作在左边添加a,右边添加c
第2,5,8,...次操作在左边添加c,右边添加a
第3,6,9,...次操作在左边添加b,右边添加b
第0次操作之后,字符串是b
第1次操作之后,字符串是abc
第2次操作之后,字符串是cabca
第3次操作之后,字符串是bcabcab
输入一个字符串,问是第几次操作之后的,无解输出-1

abc022_a Best Body

https://atcoder.jp/contests/abc022/tasks/abc022_a
输入N,S,T
接下来N个数字表示体重
第一个数字表示第一天的体重
除了第一个数字之外,表示当天减前一天体重的差
问有多少天体重在S和T之间

abc022_b Bumble Bee

https://atcoder.jp/contests/abc022/tasks/abc022_b
输入n和n个数字,输出n-不同数字的个数。

abc022_c

输入一个无向图,从1出发,走一个环,不经过重复的点重复的边最后回到1,问最小长度是多少?

abc021_a 足し算

https://atcoder.jp/contests/abc021/tasks/abc021_a
输入一个N
把N拆成若干个二的次幂的和,输出任意一组解即可

把N拆成N个1就可以了

abc021_b 嘘つきの高橋くん

https://atcoder.jp/contests/abc021/tasks/abc021_b
输入n
输入a,b
输入k
输入k个整数pi
问a,b和k个整数pi是不是两两不同

abc020_a クイズ

https://atcoder.jp/contests/abc020/tasks/abc020_a
只会输入1或2
输入 1
输出 ABC
输入 2
输出 chokudai

abc020_b 足し算

https://atcoder.jp/contests/abc020/tasks/abc020_b
输入两个十进制整数,输出这两个数字连起来的2倍

abc019_1 高橋くんと年齢

https://atcoder.jp/contests/abc019/tasks/abc019_1
输入三个数a, b, c 输出中位数

abc019_2 高橋くんと文字列圧縮

https://atcoder.jp/contests/abc019/tasks/abc019_2
文字压缩,把连续相同的字母,换成字母和出现次数
比如aabbbaad会变成a2b3a2d1

abc019_3 高橋くんと魔法の箱

https://atcoder.jp/contests/abc019/tasks/abc019_3
输入一个数组,每个数字的质因数2都约掉,问剩下多少个不同的数字?

abc018_1 豆まき

https://atcoder.jp/contests/abc018/tasks/abc018_1
输入三个不同的数字,输出他们的排名

abc018_2 文字列の反転

https://atcoder.jp/contests/abc018/tasks/abc018_2
输入一个字符串,操作数n和n个操作,每个操作是一个区间,翻转这个区间
输出n个操作之后的结果

abc017_1 プロコン

https://atcoder.jp/contests/abc017/tasks/abc017_1
输入s1,e1,s2,e2,s3,e3
输出(s1*e1+s2*e2+s3*e3)/10

abc017_2 choku語

https://atcoder.jp/contests/abc017/tasks/abc017_2
输入一个字符串,问是不是由 ch o k u 组成,输出YES或NO

abc016_1 12月6日

https://atcoder.jp/contests/abc016/tasks/abc016_1
输入M和D
问 M%D 是否为0

abc016_2 A±B Problem

https://atcoder.jp/contests/abc016/tasks/abc016_2
输入A,B,C三个数字,问A和B做加减中的什么运算能得到C?
如果只能加法,输出+
如果只能减法,输出-
如果加减法都可以,输出?
如果加减法都不可以,输出!

abc015_1 高橋くんの研修

https://atcoder.jp/contests/abc015/tasks/abc015_1
输入两个字符串,输出其中长度较长的

abc015_2 高橋くんの集計

https://atcoder.jp/contests/abc015/tasks/abc015_2
输入n个数字
求和为s
求正数个数为c
输出s除以c上取整

abc014_1 けんしょう先生のお菓子配り

https://atcoder.jp/contests/abc014/tasks/abc014_1
输入a和b,问在b的基础上至少加多少,可以让b是a的倍数

abc014_2 価格の合計

https://atcoder.jp/contests/abc014/tasks/abc014_2
一共N个物品,第i个物品价格是Ai,打算买X集合的物品
如果X的第i位是1,那么买第i个物品
如果X的第i位是0,那么不买第i个物品
问一共花多少钱

abc014_3

https://atcoder.jp/contests/abc014/tasks/abc014_3
输入n个区间,求出包含次数最多的数字被包含几次?

abc013_1 A

https://atcoder.jp/contests/abc013/tasks/abc013_1
输入一个大写字母,输出这几个大写字母。A1Z26

ASCII

abc013_2 錠

https://atcoder.jp/contests/abc013/tasks/abc013_2
输入两个1位数a和b
每次操作可以加一或者减一
特别的0可以操作成9,9可以操作成0
问从a操作到b至少需要几步?

abc012_1 スワップ

https://atcoder.jp/contests/abc012/tasks/abc012_1
输入 a, b
输出 b, a
cin >> a >> b;
cout << b << " " << a << endl;

abc012_2 入浴時間

fhttps://atcoder.jp/contests/abc012/tasks/abc012_2
输入秒数,转化为小时分钟秒,输出hh:mm:ss

abc011_1 来月は何月?

https://atcoder.jp/contests/abc011/tasks/abc011_1
输入n
输出n月的下一个月是什么

abc011_2 名前の確認

https://atcoder.jp/contests/abc011/tasks/abc011_2
输入一个字符串,从0开始下标,偶数位大写,奇数位小写,输出

abc010_1 ハンドルネーム

https://atcoder.jp/contests/abc010/tasks/abc010_1
输入一个字符串S,输出S和两个字母pp

abc010_2 花占い

https://atcoder.jp/contests/abc010/tasks/abc010_2
输入n个正整数
我们讨厌 模2余0 或 模3余2 的数字
每次操作可以把某个数减少1,
问至少操作多少次,可以让所有数字都不讨厌

abc009_1 引越し作業

https://atcoder.jp/contests/abc009/tasks/abc009_1
要双面打印n页,需要多少张纸
cout<<(n+1)/2<<endl;

abc009_2 心配性な富豪、ファミリーレストランに行く。

https://atcoder.jp/contests/abc009/tasks/abc009_2

abc008_1 アルバム

https://atcoder.jp/contests/abc008/tasks/abc008_1
输入S和T
问有多少个整数 在 S 和 T 之间 S<= .. <=T
cout<<T-S+1<<endl;

abc008_2 投票

https://atcoder.jp/contests/abc008/tasks/abc008_2
输入N个人的投票选择,问哪个选择得票最多?
如果有多个得票最多的,输出任意一个?

abc007_1 植木算

https://atcoder.jp/contests/abc007/tasks/abc007_1
输入n,输出n-1

abc007_2 辞書式順序

https://atcoder.jp/contests/abc007/tasks/abc007_2
输入一个字符串,输出一个字典序更小的字符串
如果不存在,输出a

abc006_1 世界のFizzBuzz

https://atcoder.jp/contests/abc006/tasks/abc006_1
输入n,问是不是3的倍数,输出YES或NO

abc006_2 トリボナッチ数列

https://atcoder.jp/contests/abc006/tasks/abc006_2
定义数列aa[1]=0a[2]=0a[3]=1a[i]=a[i-1]+a[i-2]+a[i-3]
输入n,输出a[n]%10007

abc005_1 おいしいたこ焼きの作り方

https://atcoder.jp/contests/abc005/tasks/abc005_1
输入x和y,输出y除以x下取整

abc005_2 おいしいたこ焼きの食べ方

https://atcoder.jp/contests/abc005/tasks/abc005_2
输入N和N个数字,输出最小值

abc004_1 流行

https://atcoder.jp/contests/abc004/tasks/abc004_1
输入n,输出2*n

abc004_2 回転

https://atcoder.jp/contests/abc004/tasks/abc004_2
输入4x4的字符矩阵,旋转180度,输出

abc003_1 AtCoder社の給料

https://atcoder.jp/contests/abc003/tasks/abc003_1
输入n,输出(n+1)*5000

abc003_2 AtCoderトランプ

https://atcoder.jp/contests/abc003/tasks/abc003_2
输入两个字符串,可以把@替换为atcoder中的任意一个字符
如果两个字符串可以相等,输出You can win,否则输出You will lose

abc002_1 正直者

https://atcoder.jp/contests/abc002/tasks/abc002_1
输入X,Y,输出max(X,Y)

abc002_2 罠

https://atcoder.jp/contests/abc002/tasks/abc002_2
输入一个字符串,删去所有的元音aeoiu输出

abc001_1 積雪深差

https://atcoder.jp/contests/abc001/tasks/abc001_1
输入H1,H2,输出H1-H2

abc001_2 視程の通報

https://atcoder.jp/contests/abc001/tasks/abc001_2
输入N
如果N<100输出00
否则如果N<=5000输出N/100
否则如果N<=30000输出N/1000+50
否则如果N<=70000输出N/5000+74
否则输出89

abc001_3 風力観測

https://atcoder.jp/contests/abc001/tasks/abc001_3
输入风向和风力

转化为NEWS和级别

arc138_a Larger Score

https://atcoder.jp/contests/arc138/tasks/arc138_a
输入N个数字
目标让前K个数字之和变大
执行一次操作可以交换相邻两个数字
问至少执行几次操作

arc138_b 01 Generation

https://atcoder.jp/contests/arc138/tasks/arc138_b
输入一个长度为n的01序列,问能不能通过以下两种操作生成
序列中0变成1,1变成0,并且在开始加一个0
序列结尾加一个0

如果只做第一个操作,只能得到 0 1 0 1 0 1 ... 这样的序列
序列必须以0开始,0 1交替若干次

arc137_a Coprime Pair

https://atcoder.jp/contests/arc137/tasks/arc137_a
输入L和R找到(x,y),满足L<=x<y<=R并且x和y互质
问y-x最大

arc137_b Count 1's

https://atcoder.jp/contests/arc137/tasks/arc137_b
输入一个01数组,选一个区间,0改成1,1改成0
问修改之后的数组,1的个数有多少种可能

arc137_c Distinct Numbers

n个非负整数,初始互不相同
双方轮流操作
每次把最大的数字改成一个较小且没有出现过的数字
谁无法操作谁就输了,问先手赢还是后手赢?

arc136_a A ↔ BB

https://atcoder.jp/contests/arc136/tasks/arc136_a
输入一个字符串S,可以把A替换成BB,也可以把BB替换成A
问经过替换后字典序最小的字符串是什么?

arc131_a Two Lucky Numbers

https://atcoder.jp/contests/arc131/tasks/arc131_a
输入A和B
输入一个数字x,使得x的十进制包含A,且2x的十进制包含B

用第18位凑A,第9位空着预备进位,用第1017位凑B
答案是 A + B * 500000000

arc131_b Grid Repainting 4

https://atcoder.jp/contests/arc131/tasks/arc131_b
输入一个H行W列的字符数组
每个位置可能已经染色12345之一,也可能还未染色
你要给未染色的格子染色,使得上下左右4个方向相邻的格子颜色必须不同

arc131_c Zero XOR

https://atcoder.jp/contests/arc131/tasks/arc131_c
输入n个互不相同的数字,双方轮流删数字,如果删之后剩下的数字xor是0,那么就赢了
问先手必胜还是后手必胜

除非一步就赢,否则一定会把所有数字取完

arc130_a Remove One Character

https://atcoder.jp/contests/arc130/tasks/arc130_a
字符串删掉一个字母,问结果有多少种可能

arc130_b Colorful Lines

https://atcoder.jp/contests/arc130/tasks/arc130_b
输入H, W, C, Q
表示H行W列的矩阵,C种颜色,Q个操作
操作有两种,染一行,染一列,染色时颜色会覆盖之前的颜色
问最终每种颜色剩下多少个

不妨认为第i次染色是i,问每次染色最后剩下几个
倒序考虑,最后一次染一定全剩下了
倒数第二次染,除了最后一次染的位置,都剩下了

记录每一行/每一列是否染过颜色,需要用map

#include <bits/stdc++.h>
using namespace std;
int n, m, l, q;
set<int> r, c;
int main()
{
    cin >> n >> m >> l >> q;
    for (int i = 0; i < q; i++)
    {
        cin >> o[i] >> x[i] >> y[i];
    }
    for (int i = q - 1; i >= 0; i--)
    {
        if (o[i] == 1)
        {
            if (r.find(x[i]) == r.end())
            {
                r.insert(x[i]);
                n -= 1;
                z[y[i]] += m;
            }
        }
        else
        {
            if (c.find(x[i]) == c.end())
            {
                c.insert(x[i]);
                m -= 1;
                z[y[i]] += n;
            }
        }
    }
    for (int i = 1; i <= l; i++)
    {
        cout << z[i] << " ";
    }
}

arc128_a Gold and Silver

https://atcoder.jp/contests/arc128/tasks/arc128_a
初始1克金,0克银
第i天,1克金=Ai克银
每一天结束可以选择持有金或银
第n天结束希望持有最多的金
问如何操作,每一天可以选择交易,或不交易

arc127_a Leading 1s

https://atcoder.jp/contests/arc127/tasks/arc127_a
f(x) 表示 x 有多少个前导1
输入 n
输出 f(1) + f(2) + ... + f(n)

arc126_a Make 10

https://atcoder.jp/contests/arc126/tasks/arc126_a
用N2个2,N3个3,N4个4,问最多能凑出多少个10?

arc113_a A*B*C

https://atcoder.jp/contests/arc113/tasks/arc113_a
输入K(K<=2e5),问有多少组正整数(A,B,C)满足A*B*C<=K

arc110_a Redundant Redundancy

https://atcoder.jp/contests/arc110/tasks/arc110_a
输入n,输出1到n的lcm再加1

arc108_a Sum and Product

https://atcoder.jp/contests/arc108/tasks/arc108_a
输入S和P,问是否存在两个数字和是S,乘积是P。

arc106_a 106

https://atcoder.jp/contests/arc106/tasks/arc106_a
输入N,找到3**A+5**B==N的一组解,无解输出-1

arc085_a HSI

https://atcoder.jp/contests/arc085/tasks/arc085_a
提交做题,一共有N组数据
其中M组,每一组正确的概率是1/2
另外N-M组,正确概率是1
提交一次消耗M*1900+(N-M)*100的时间
消耗的时间是固定的,和自己对几个,错几个没有关系
问期望需要多少时间通过所有N个测试点?

因为消耗的时间和对几个错几个没关系,所以答案就是 期望提交次数 乘以 每次提交的时间
如果提交一次,有1/2的概率正确,那么期望交2次
如果提交一次,有1/4的概率正确,那么期望交4次
如果提交一次,有1/8的概率正确,那么期望交8次
如果提交一次,有1/2**m的概率正确,那么期望交2**m

arc077_a pushpush

https://atcoder.jp/contests/abc066/tasks/arc077_a
输入一个长度为n的数组a,对空数组b做n次操作
每次在b的末尾插入a[i]并且前后翻转b
问最后b是什么

arc077_b 11

https://atcoder.jp/contests/abc066/tasks/arc077_b
输入一个长度为n+1的数列,保证1到n每个数字出现一次,其中一个出现2次
问有多少个不同的长度为k的子序列(对于每个k都回答)
值一样,位置不一样,算作相同的子序列

arc067_a Factors of Factorial

https://atcoder.jp/contests/abc052/tasks/arc067_a
输入 n,输出 n! 的约数个数,模 1000000007

arc065_b Connectivity

https://atcoder.jp/contests/arc065/tasks/arc065_b
n个点,k条公路,l条铁路
对于每个点,问有多少个点和他
既可以通过公路连接,也可以通过铁路连接

agc054_a Remove Substrings

https://atcoder.jp/contests/agc054/tasks/agc054_a
输入一个字符串,每次可以删去一个首位不相同的子串,问能否把字符串删空

agc041_a Table Tennis Training

https://atcoder.jp/contests/agc041/tasks/agc041_a
输入N,A,B问至少多少次操作可以让A和B相等
一次操作可以让 A增加或减少一 并且 B增加或减少一,不能只操作一个
如果A或B等于1或N,那么可以选择不变

agc040_a ><

https://atcoder.jp/contests/agc040/tasks/agc040_a
输入一个长度n-1,只包含><的字符串,求一个长度为n的数列
使得数列中相邻两项的大小关系符合字符串的要求
问这个数列所有数字之和最小是多少

 < > > > < < > < < < < < > > > <
0 1 0 0 0 1 2 0 1 2 3 4 5 0 0 0 1
0 3 2 1 0 0 1 0 0 0 0 0 3 2 1 0 0

0 3 2 1 0 1 2 0 1 2 3 4 5 2 1 0 1

agc013_a Sorted Arrays

https://atcoder.jp/contests/agc013/tasks/agc013_a
输入一个长度为N的数组,分成连续的若干段,使每段有序
有序可以是不上升,或者不下降
问至少分成几段

agc010_a Addition

https://atcoder.jp/contests/agc010/tasks/agc010_a
输入n个数字,每次可以合并两个同奇偶的数字,变成他们的和
问能不能最终剩下一个数字?

如果奇数的个数是偶数就可以

zone2021_a UFO Invasion

https://atcoder.jp/contests/zone2021/tasks/zone2021_a
输入一个字符串S,输出ZONe在其中出现几次?

zone2021_b Sign of Friendship

https://atcoder.jp/contests/zone2021/tasks/zone2021_b
输入N,D,H
目标是距离为D,高度为H的飞碟
但中间有N个障碍物,第i个障碍物的距离是di,高度是hi
问自己要从原地升到多高,才能看到飞碟

zone2021_c MAD TEAM

https://atcoder.jp/contests/zone2021/tasks/zone2021_c
从n个人中选3个人
每个人有5个属性Ai Bi Ci Di Ei
选出的3个人的5个属性,每一个属性三个人求最大值,得到5个数字
最后的强度是5个属性的最大值中的最小值
最大化最后的强度

hhkb2020_a Keyboard

https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_a
输入Y或N,和a,b,c字母之一,如果是Y,输出大写,否则原封不动输出

hhkb2020_b Futon

https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_b
输入H行W列的字符数组,问有多少对上下或左右相邻的.

hhkb2020_c Neq Min

https://atcoder.jp/contests/hhkb2020/tasks/hhkb2020_c
输入N个数字,对于每个前缀回答,最小的没有出现过的非负整数是什么?

abl_a Repeat ACL

https://atcoder.jp/contests/abl/tasks/abl_a
输入K,输出K次ACL

abl_b Integer Preference

https://atcoder.jp/contests/abl/tasks/abl_b
输入A,B,C,D
问是否有整数x满足
A<=x<=BC<=x<=D
输出Yes或No

abl_c Connect Cities

https://atcoder.jp/contests/abl/tasks/abl_c
输入N个点,M条边,问连通块个数

panasonic2020_a Kth Term

https://atcoder.jp/contests/panasonic2020/tasks/panasonic2020_a
问题目中的数字第K个数字是什么?

panasonic2020_b Bishop

https://atcoder.jp/contests/panasonic2020/tasks/panasonic2020_b
H行W列的国际象棋棋盘,象从左上角能到多少个格子

panasonic2020_c Sqrt Inequality

https://atcoder.jp/contests/panasonic2020/tasks/panasonic2020_c
输入A B C
sqrt(A)+sqrt(B)<sqrt(c) 是否成立

不能直接开根号,必须用整数计算!

aising2019_a Bulletin Board

https://atcoder.jp/contests/aising2019/tasks/aising2019_a
问在N行N列的棋盘中,放一个H行W列的棋盘有多少种方案?

aising2019_b Contests

https://atcoder.jp/contests/aising2019/tasks/aising2019_b
输入N个题目,第i个题目分数是Pi,一场比赛需要三个题
第一题<=A
A<第二题<=B
B<第三题
问最多组多少场比赛

soundhound2018_summer_qual_a F

https://atcoder.jp/contests/soundhound2018-summer-qual/tasks/soundhound2018_summer_qual_a
输入a和b,如果a+b==15输出+,如果a*b==15输出*,如果都不符合输出x

soundhound2018_summer_qual_b

https://atcoder.jp/contests/soundhound2018-summer-qual/tasks/soundhound2018_summer_qual_b
输入一个字符串S和w,字符串下标从0开始
输出所有w的倍数的位置的字符

keyence2021_a Two Sequences 2

https://atcoder.jp/contests/keyence2021/tasks/keyence2021_a
输入两个数组,对于每个n,找到i<j<=n使得a[i]*b[j]最大

tokiomarine2020_a Nickname

https://atcoder.jp/contests/tokiomarine2020/tasks/tokiomarine2020_a
输入一个字符串,输出前三个字母

nomura2020_a Study Scheduling

https://atcoder.jp/contests/nomura2020/tasks/nomura2020_a
从H1时M1分,到H2时M2分,选出连续K分钟,问起点可能的区间有多长?

nomura2020_b Postdocs

https://atcoder.jp/contests/nomura2020/tasks/nomura2020_b
一个只包含D和P的字符串,得分是D出现的次数加上PD出现的次数
输入一个包含D和P和?的字符串,把?替换成D或P,问最多得多少分?

hitachi2020_a Hitachi String

https://atcoder.jp/contests/hitachi2020/tasks/hitachi2020_a
输入一个字符串问是不是由hi重复若干次得到?

hitachi2020_b Nice Shopping

https://atcoder.jp/contests/hitachi2020/tasks/hitachi2020_b
输入A种冰箱和B种微波炉的价格
输入M个优惠券吗,每个优惠券包含3个数字x y c
如果同时购买第x个冰箱和第y个微波炉,可以优惠c元
同时只能使用一张优惠券
目标买一个冰箱一个微波炉问最少多少钱?

keyence2020_a Painting

https://atcoder.jp/contests/keyence2020/tasks/keyence2020_a
H行,W列的矩阵,每次操作可以染黑一整行或一整列,问至少操作多少次,可以染黑至少N个格子

dwacon6th_prelims_a Falling Asleep

https://atcoder.jp/contests/dwacon6th-prelims/tasks/dwacon6th_prelims_a
输入N首歌和时间,第i首歌名字Si,时间Ti
问歌曲X之后(不包括X)所有歌的时间之和是多少

ddcc2020_qual_a DDCC Finals

https://atcoder.jp/contests/ddcc2020-qual/tasks/ddcc2020_qual_a
输入两个名次
第1名300000元奖金
第2名200000元奖金
第3名100000元奖金
特别的如果两个名次都是第一,会获得额外400000元奖金
输入两个名次,输出总奖金额度

ddcc2020_qual_b Iron Bar Cutting

https://atcoder.jp/contests/ddcc2020-qual/tasks/ddcc2020_qual_b
输入一个数组,从某个位置切开,分成前后两部分
问两部分之和的差最小是多少

nikkei2019_2_qual_a Sum of Two Integers

https://atcoder.jp/contests/nikkei2019-2-qual/tasks/nikkei2019_2_qual_a
输入正整数N,问有多少种方法把N分成两个不同的正整数(不计顺序,1+2=3和2+1=3算同一种方案)

jsc2019_qual_a Takahashi Calendar

https://atcoder.jp/contests/jsc2019-qual/tasks/jsc2019_qual_a
m月d日 被称为乘积日,当且仅当
d的个位和十位都>=2
d的个位乘以十位是m
如果日历中一共有M个月,每个月有D天,问一年有多少个乘积日

diverta2019_2_a Ball Distribution

https://atcoder.jp/contests/diverta2019-2/tasks/diverta2019_2_a
N个球分给K个人,问拿到球最多的和最少的能差多少个球?

m_solutions2019_a Sum of Interior Angles

https://atcoder.jp/contests/m-solutions2019/tasks/m_solutions2019_a
输入N,问正N边形内角和是多少度?

m_solutions2019_b Sumo

https://atcoder.jp/contests/m-solutions2019/tasks/m_solutions2019_b
输入一个字符串,问其中的x的个数是否小于等于7,输出YES或NO

diverta2019_a Consecutive Integers

https://atcoder.jp/contests/diverta2019/tasks/diverta2019_a
问从1到N中连续选K个数字有多少种方案?

exawizards2019_a Regular Triangle

https://atcoder.jp/contests/exawizards2019/tasks/exawizards2019_a
输入三条边的长度,问是不是等边三角形

exawizards2019_b Red or Blue

https://atcoder.jp/contests/exawizards2019/tasks/exawizards2019_b
输入一个字符串,问是不是R比B多,输出Yes或No

yahoo_procon2019_qual_a Anti-Adjacency

https://atcoder.jp/contests/yahoo-procon2019-qual/tasks/yahoo_procon2019_qual_a
问能不能从1到n中选出k个数字,其中没有两个数字相差1

yahoo_procon2019_qual_b Path

https://atcoder.jp/contests/yahoo-procon2019-qual/tasks/yahoo_procon2019_qual_b
1,2,3,4一共四个点,输入三条边,问是否构成一条链
输入的边是无向的,保证连通

nikkei2019_qual_a Subscribers

https://atcoder.jp/contests/nikkei2019-qual/tasks/nikkei2019_qual_a
N个人做两道题,A个人做对了第一题,B个人做对了第二题
问至多和至少有多少人做对了两个题

nikkei2019_qual_b Touitsu

https://atcoder.jp/contests/nikkei2019-qual/tasks/nikkei2019_qual_b
输入三个长度相等的字符串,每次操作可以修改其中一个字符串的一个位置
目标是让3个字符串相同,问至少多少次操作

keyence2019_a Beginning

https://atcoder.jp/contests/keyence2019/tasks/keyence2019_a
输入四个一位数,问重排之后能不能变成1974,输出Yes或No

dwacon5th_prelims_a Thumbnail

https://atcoder.jp/contests/dwacon5th-prelims/tasks/dwacon5th_prelims_a
输入n个数字,输出最接近平均数的数字的下标,下标从0开始,如果有多个最接近的,输出最小的下标

exawizards2019_a Regular Triangle

https://atcoder.jp/contests/exawizards2019/tasks/exawizards2019_a
输入三角形的三条边,如果是等边三角形,输出Yes,否则输出No

exawizards2019_b Red or Blue

https://atcoder.jp/contests/exawizards2019/tasks/exawizards2019_b
输入一个字符串,问R的个数是否大于B的个数,输出Yes或No

yahoo_procon2019_qual_a Anti-Adjacency

https://atcoder.jp/contests/yahoo-procon2019-qual/tasks/yahoo_procon2019_qual_a
输入N和K,问能不能从1到N中选K个数字,使得没有任何两个数字的差是1

yahoo_procon2019_qual_b Path

https://atcoder.jp/contests/yahoo-procon2019-qual/tasks/yahoo_procon2019_qual_b
四个点,三条边,问是不是连成了一条路径

nikkei2019_qual_a

https://atcoder.jp/contests/nikkei2019-qual/tasks/nikkei2019_qual_a
N个人回答两个Yes No的问题
A个人第一个问题回答Yes
B个人第二个问题回答Yes
问至多有多少人两个都是Yes?
问至少有多少人两个都是Yes?

diverta2019_2_a Ball Distribution

https://atcoder.jp/contests/diverta2019-2/tasks/diverta2019_2_a
N个球分给K个人,每人至少一个球,问拿球最多的减去拿球最少的差值,最大是多少?

diverta2019_a Consecutive Integers

https://atcoder.jp/contests/diverta2019/tasks/diverta2019_a
输入N和K,从1到N中选K个连续的数字,问有多少种选法?

code_festival_2017_quala_b fLIP

https://atcoder.jp/contests/code-festival-2017-quala/tasks/code_festival_2017_quala_b
n行m列的白色网格
每次操作可以翻转一行或一列的颜色,翻转就是黑变白,白变黑
问能不能通过若干次操作,使得恰好有k个黑色

[MUMETOC]

https://atcoder.jp/contests/abc254/submissions/32222821
大家可以想一想为什么这可能超时

abc254_f

https://atcoder.jp/contests/abc254/tasks/abc254_f
一个n行m列的数字,第行第j列是a[i]+b[j]
每次问一个矩形区域的GCD

求这个区域的gcd
a[2]+b[5] a[2]+b[6] a[2]+b[7] a[2]+b[8]
a[3]+b[5] a[3]+b[6] a[3]+b[7] a[3]+b[8]
a[4]+b[5] a[4]+b[6] a[4]+b[7] a[4]+b[8]

等价于
a[2]+b[5] b[6]-b[5] b[7]-b[6] b[8]-b[7]
a[3]+b[5] b[6]-b[5] b[7]-b[6] b[8]-b[7]
a[4]+b[5] b[6]-b[5] b[7]-b[6] b[8]-b[7]

等价于
a[2]+b[5] b[6]-b[5] b[7]-b[6] b[8]-b[7]
a[3]+b[5]
a[4]+b[5]

等价于
a[2]+b[5] b[6]-b[5] b[7]-b[6] b[8]-b[7]
a[3]-a[2]
a[4]-a[3]


如果输入的范围是h1 h2 w1 w2
a[h1]+b[w1] b[w1+1]-b[w1] ... b[w2]-b[w2-1]
a[h1+1]-a[h1]
...
a[h2]-a[h2-1]

分别求
a[h1]+b[w1] 
gcd(b[w1+1]-b[w1] ... b[w2]-b[w2-1])
gcd(a[h1+1]-a[h1] ... a[h2]-a[h2-1])

abc247_e Max Min

https://atcoder.jp/contests/abc247/tasks/abc247_e
输入一个长度为n个数字,和最大值X,和最小是Y
问数组中有几个区间最大值是X且最小值是Y

所有数字分为>Y==Y<X==XX<..<Y五种情况
>Y<X的位置一定不能选
选一个区间,包含至少一个==Y,包含至少一个==X,问有多少种方案
问以每个位置作为结尾,有多少个方案
对于每个位置,找自己之前的最近的X是多少,最近的Y是多少,至多选多少个数字没有>Y<X的情况

abc247_f Cards

https://atcoder.jp/contests/abc247/tasks/abc247_f
有n张牌,第i张牌正面是Pi,反面是Qi。保证Pi和Qi是1到n的排列
从所有的牌中选一个子集,使得从1到n的每个数字,都出现过(正面或反面)

把每张牌看做是连接Pi和Qi的一条边
因为Pi和Qi都是排列,所以整个图一定是由若干环组成的
对于每个环来说,选取一个子集,不能有连续的两条边不选
这是一个经典问题,答案是Lucas Number,是以2 1 3 4 7 ...开始的Fibonacci数列
不同环之间不会互相影响,把不同环的答案乘起来就可以了

abc246_a Four Points

https://atcoder.jp/contests/abc246/tasks/abc246_a
输入矩形的四个顶点中的三个,问最后一个顶点是什么?

abc246_b Get Closer

https://atcoder.jp/contests/abc246/tasks/abc246_b
从点(0,0)向点(A,B)的方向走1的距离,问会到哪个点,输出小数
保证点(0,0)到点(A,B)之间的距离大于等于1

要找到两个数字(X,Y)
满足X*X+Y*Y=1X/Y=A/B

abc246_c Coupon

https://atcoder.jp/contests/abc246/tasks/abc246_c
买n个商品,第i个商品价格是ai,有k个优惠券,每个优惠券可以优惠X元
优惠券可以在同一个商品上叠加,但是价格不能为负数
问最优情况下买这n个商品需要多少钱?

abc246_d 2-variable Function

https://atcoder.jp/contests/abc246/tasks/abc246_d
输入N,找到一个数字X,使得X>=N且存在非负整数a和b使得X=a*a*a+a*a*b+a*b*b+b*b*b
问最小的X是多少?

双指针法

首先假设a=0求出b最小是多少(找到最小的b使得X>=N
(直接cbrt相当于找到最大的b使得X<=N
所以如果最优解就是a=0bcbrt(N)上取整,就会出错

while a <= b:
    if X >= N:
        b -= 1
    else:
        a += 1

abc246_e Bishop 2

https://atcoder.jp/contests/abc246/tasks/abc246_e
国际象棋棋盘,有障碍物,问象从起点到终点最少需要几步?
先输入N,表示行数和列数
然后输入起点的坐标Ax和Ay
然后输入终点的坐标Bx和By
然后输入N行,表示棋盘的初始状态,其中.表示空,#表示障碍物
国际象棋的象,走斜线,任意多步

直接BFS会超时
BFS的时间复杂度是什么?
状态数*转移数

在这个题目中,状态数约为n*n/2,转移数(从一个位置可以走到多少个位置)大概2*n
所以直接BFS的时间复杂度是n*n*n是超时的

4
4 2
1 1
...#
...#
...#
...#

https://atcoder.jp/contests/abc246/submissions/30658548
if(m[ny][nx]=='*')continue;
这个方法没有问题,只会超时,不会WA

https://atcoder.jp/contests/abc246/submissions/30647486

else if(d[x][y]<=d[w.u][w.v]) break;

如果之前到这里的距离是d[x][y]
这次到这里的距离是d[w.u][w.v]+1
如果d[x][y]<d[w.u][w.v]+1
那么就没有必要继续沿着这个方向走下去了,因为从d[x][y]出发,即使再走一步,也和当前距离d[w.u][w.v]+1相等

边权只有0和1的BFS,用双端队列做

abc246_f typewriter

https://atcoder.jp/contests/abc246/tasks/abc246_f
有一个打字机,打字机有N行,第i行有Si这些字母(不同行可能有相同的字母)
打字的过程是选一行(之后不能在变了)只能用这行的字母,生成一个长度为L的字符串
问一共有多少种可能字符串(可能选不同行能生成同一个字符串,只算一次)

有A B C三行
答案是

+ A能打出的字符串
+ B能打出的字符串
+ C能打出的字符串
- A和B都能打出的字符串
- A和C都能打出的字符串
- B和C都能打出的字符串
+ A和B和C都能打出的字符串

abc245_d Polynomial division

https://atcoder.jp/contests/abc245/tasks/abc245_d
多项式除法
输入一个N次多项式A
输入一个N+M次多项式C
找到一个次数是M的多项式B
使得多项式A乘以多项式B等于多项式C
不需要取模,可能有负数,保证有解

2 1
12 14 8 2

    6 4 2
----------
12 14 8 2
12  6
    8 8 2
    8 4
      4 2
      4 2

abc245_e Wrapping Chocolate

https://atcoder.jp/contests/abc245/tasks/abc245_e
n个巧克力,第i个巧克力宽A[i]B[i]
m个盒子,第i个盒子宽C[i]D[i]
每个盒子至多装1个巧克力
如果A[i]<=C[j]&&B[i]<=D[j]那么第i个巧克力可以被装进第j个盒子中
问能不能把所有巧克力都装进盒子

把巧克力和盒子一起按宽从大到小排序
如果盒子和巧克力宽一样,盒子在前

依次扫描所有的盒子和巧克力
    如果这是一个盒子,multiset中加入这个盒子的长度
    如果这是一个巧克力,在multiset中找最小能包含这个巧克力的长度的盒子
        (因为multiset中的盒子都是在之前加入的,所以盒子的宽度一定大于等于当前巧克力的宽度)
        这个题只问了能不能全找到,如果找不到直接就无解了
        其实也可以问最多几个巧克力可以被放进盒子里

abc244_a

输入字符串,输出最后一个字符

abc244_c Yamanote Line Game

https://atcoder.jp/contests/abc244/tasks/abc244_c
输入一个数字N,你和对方轮流说出一个1到2*N+1的数字
说过的数字不能再说
谁没有数字说,谁就输了
对方输入0,说明你赢了
fflush(stdout)

如果使用 cout 换行用endl还是'\n'无所谓,也不需要 flush
https://atcoder.jp/contests/abc244/submissions/30425407
https://atcoder.jp/contests/abc244/submissions/30425434

如果使用 printf 必须fflush(stdout)
https://atcoder.jp/contests/abc244/submissions/30425627
如果不fflush(stdout)会TLE
https://atcoder.jp/contests/abc244/submissions/30425609

输出数字之后一定要换行,不换行出错的例子:
https://atcoder.jp/contests/abc244/submissions/30280009

是否TLE与是否加 ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 无关

abc211_a

输入 A 和 B
输出实数 (A-B)/3+B

输入输出

abc211_b

输入 4 个字符串,问是不是 H 2B 3B HR 每个都出现且只出现一次

排序

abc211_c

输入字符串 S ,问 chokudai 作为子序列在不同的位置出现几次?

简单DP

abc211_d

输入一个无向图,边权都是1
问 1 到 n 最短路的条数

直接BFS

abc211_e

输入一个N * N的地图
从中选取一个四连通大小为K的区域
问有多少种选法

set vector 爆搜

abc211_f
输入 N 个多边形
Q个询问,询问一个格子被覆盖了多少次

离线树状数组

arc137_c Distinct Numbers

n个非负整数,初始互不相同
双方轮流操作
每次把最大的数字改成一个较小且没有出现过的数字
谁无法操作谁就输了,问先手赢还是后手赢?

如果 最大的两个数字,前面有相同个数的偶数个空位 先手必败
否则 先手必胜

要证明2个事情
第一个事情:
如果 最大的两个数字,前面有相同个数的偶数个空位
操作一次之后
最大的两个数字,前面有相同个数的偶数个空位 一定不成立

第二个事情:
对于任何一个不满足
最大的两个数字,前面有相同个数的偶数个空位
条件的局面
操作一次之后一定就满足了!

考虑到绝大多数 codeforces 题目在 luogu 上都有翻译,这个列表不会经常更新

[MUMETOC]

CF1672A Log Chopping

https://codeforces.com/problemset/problem/1672/A
输入N个正整数,双方轮流操作
每次操作可以把一个已有的正整数,拆成两个正整数之和
谁无法才做谁就输了(面对全是1的局面,就无法继续拆数了)
如果先手必败输出 maomao90
如果先手必胜输出 errorgorn

CF1672B I love AAAB

https://codeforces.com/problemset/problem/1672/B
若干A加上一个B,是好字符串,单独一个B不是好字符串
初始是空串,每次可以选任意位置,插入一个好字符串
问最终结果能不能是输入的字符串,输出Yes或No

如果以A结尾,那么NO
如果存在前缀,B的个数大于A的个数,那么NO

CF1672C Unequal Array

定义一个数组的相同度,是相邻相等的元素对数
选择两个相邻的元素,同时改成一个新的数字x
输入一个数字,经过若干次操作,让相同度<=1
问至少几次操作

CF1669D Colorful Stamp

https://codeforces.com/problemset/problem/1669/D
初始一个全是W的字符串
每次操作可以选相邻两个位置,一个改成R,一个改成B
(选过的位置可以重复选)
(不能只选一个,必须是选相邻的2个位置)
(这两个位置可以改成BR也可以改成RB)
问最终结果是否可能是输入的字符串?

CF1660B Vlad and Candies

https://codeforces.com/problemset/problem/1660/B
多组数据
每组数据输入一个长度为n的数组
每次从数组中选一个最大值自减1
直到数组中所有数字变为0
不希望有一个数字连续两次减少1
问能不能做到,如果可以输出Yes,如果做不到输出No

CF1656A Good Pairs

https://codeforces.com/contest/1656/problem/A
输入一个数组,找到两个下标(i,j),使得对于所有的k都满足
abs(a[i] - a[k]) + abs(a[k] - a[j]) == abs(a[i] - a[j])

CF1656B Subtract Operation

https://codeforces.com/contest/1656/problem/B
输入一个数组a每次从中选择一个数字x,删去这个数字,其他数字都a[i]-=x
问操作n-1次之后,能不能恰好剩下k

CF1656C Make Equal With Mod

https://codeforces.com/contest/1656/problem/C
输入一个数组a,每次可以选一个数字x(x>=2),所有数字a[i]%=x
问能不能让所有数字都一样

CF1654A

https://codeforces.com/contest/1654/problem/A
输入一个数组,输出最大+次大

CF1654B

https://codeforces.com/contest/1654/problem/B
输入一个字符串
while 第一个字母在后面也出现过
删掉第一个字母
输出这个字符串

CF1654C

https://codeforces.com/contest/1654/problem/B
输入n个数字 ai
初始是 s=sum(a)
做 n-1 次操作
每次操作可以选一个 w 并把他分成 (w/2) 上取整 和 (w/2) 下取整 两个数字
问是否可能分出输入的n个数字

CF1649A

https://codeforces.com/contest/1649/problem/A
输入长度为n的数组,包含0和1
第一个和最后一个位置一定是1,从第一个位置走到最后一个位置,不能经过0
可以花x的钱,从下标i跳到下标i+x,但是只能跳一次
问从下标1到下标n最少花多少钱?
(注意:如果i和i+1都是陆地,那么从i移动到i+1不需要花钱)

CF1649B

https://codeforces.com/contest/1649/problem/B
n个人之间互相传球,已知第i个人传出了ai次球
问至少有多少个球才可能发生这样的情况

全是0,答案是0
如果 最大的数字 大于 其他所有数字的和,那么答案是 最大的数字-其他所有数字的和
答案是1

CF1648A Weird Sum

https://codeforces.com/contest/1648/problem/A
输入n*m的矩阵,每个位置有一个颜色
对于每对颜色相同的格子,求曼哈顿距离值之和

CF1648B

https://codeforces.com/contest/1648/problem/B
如果数组中任意两个数字的商(大的除以小的下取整)还在这个数组中,
那么称这个数组是integral
输入一个数组,判断是不是integral

CF1646C

https://codeforces.com/problemset/problem/1646/C
用阶乘和 2的次幂凑n,问至少需要几个数字?

CF1634A

https://codeforces.com/problemset/problem/1634/A
操作次数0,答案是1
回文串,答案是1
否则答案是2

CF1634B

https://codeforces.com/problemset/problem/1634/B
注意到Alice和Bob的奇偶性永远不同

CF1634C

https://codeforces.com/problemset/problem/1634/C

1 3 5 7 9
2 4 6 8 10
11 13 15 17 19
12 14 16 18 20
偶数行有解

奇数行,如果只有一列,有解

第i行,第j列
i / 2 * 2 * m + i % 2 + 2 * j + 1

CF1633A

https://codeforces.com/problemset/problem/1633/A
n
print(n//1010+7-n//1010%7)

CF1633C

https://codeforces.com/problemset/problem/1633/C

怪物生命值 hm
升级了i次攻击
主角攻击力 (dc+i*w)
主角被攻击次数 (hm+(dc+i*w)-1)/(dc+i*w)-1
主角被攻击次数 (hm-1)/(dc+i*w)
怪物攻击力 dm
主角损失生命值 (hm-1)/(dc+i*w)*dm
主角的生命值 (hc+(k-i)*a)
问是否存在i使得
(hm-1)/(dc+i*w)*dm < (hc+(k-i)*a)

CF1633D

https://codeforces.com/problemset/problem/1633/D
设从1操作到i需要g[i]次,用动态规划求
发现g[i]最大是12,所以如果k>12*n,可以直接把k改为12*n
然后就是一般的背包了,物品重量是操作次数b[g[i]],物品价值是c[i]

https://codeforces.com/contest/1633/submission/145277902

CF1632C

https://codeforces.com/problemset/problem/1632/C

a|=b 至多执行1次,因为执行后一定a>=b,为了让他们相等只会执行b+=1
核心在于a|=b之前,a+=1多少次,b+=1多少次
直觉上来说,数字a中1越少越少,数字b中1越多越好
因为如果a里面没有1,b里面有1,那么a可以通过a|=b获得

比如初始a=10=0b1010,那么把a加到11=0b1011是没有意义的,只是多了1

有可能需要在a|=b之前,先执行b+=1,比如

a=4 = 0b0100
b=11= 0b1011

发现在a|=b之后执行b+=1没有必要
找到cd,满足c>=a&&d>=b,最小化c+(c|d)

输入 c 和 b
找到d,d>=b且 c|d 最小

找到 c 是 1 且 b 是 0 的最高位
把b,比这一位低的都改成0
c & ~ b

CF1627A Not Shading

https://codeforces.com/contest/1627/problem/A
输入n行m列字符矩阵
每次操作可以选一个B,把一行或一列都改成B
目标让第r行,第c列的格子变成B
问最少操作几次

已经是黑的0次
同一行或列有黑的1次
全局全白-1无解
其他2次

CF1627B Not Sitting

https://codeforces.com/contest/1627/problem/B
甲和乙
甲Tina
乙Rahul
n行m列的座位
甲选k个位置乙不准坐
乙选一个可以坐的位置左下
甲选一个位置坐下

甲希望离乙越远越好
乙希望离甲越近越好
远近都是曼哈顿距离

对于每个k都回答一下

甲希望离乙越远越好,所以会选四个角之一
乙知道甲会选四个角之一,对于乙来说,每个位置的距离,是到四个角最远的一个角的距离
甲优选不让乙坐距离小的位置
乙坐甲没有不准做的,距离最小的位置

(把所有位置按照距离排序)
每个位置到四个角距离的最大值
5 4 5
4 3 4
4 3 4
5 4 5

排序这个数组,输出就可以了

CF1627C Not Assigning

https://codeforces.com/contest/1627/problem/C
输入一个树
你来定边权
要求边权都是质数
并且有公共端点的2条边和也是质数

如果存在一个点,度数>2,直接无解
接下来这个树一定是一条链,按照2 3 2 3 2 顺序赋值即可

CF1600E Array Game

https://codeforces.com/problemset/problem/1600/E
https://www.luogu.com.cn/problem/CF1600E
Alice 和 Bob 正在玩一个游戏。他们得到了一个长度为 N 由整数组成的数组 A。
他们正在一起建立一个序列。在开始的时候,这个序列是空的。
在一个回合中,玩家可以从数组的左边或右边移出一个数字,并将其添加到序列的右侧。
规则是:他们所建立的序列必须是单调递增的。赢家就是是做出最后一步的玩家。
Alice 是第一个玩的。假设他们都以最佳方式进行游戏的情况下,谁能赢得游戏?

https://codeforces.com/contest/1551

https://codeforces.com/contest/1551/problem/A
输入一个数字 n
找到 c1 和 c2
使得 c1 + 2 * c2 == n
最小化 abs(c1 - c2)
输入任意解(其实解唯一

https://codeforces.com/contest/1551/problem/B1
https://codeforces.com/contest/1551/problem/B2

输入一个字符串 / 数字串
输入一个颜色数
给每个位置染色,要求相同的字母不能染成相同的颜色
不同颜色出现的次数必须相同
问颜色出现几次
输出方案

https://codeforces.com/contest/1551/problem/C
输入 n 个单词
从中选出一些单词
如果其中存在一个字母,出现次数大于一半,那么这个选出的子序列是 interesting 的
问 interesting 的子序列最长多长?

https://codeforces.com/contest/1551/problem/D1
https://codeforces.com/contest/1551/problem/D2
如果 n * m 是偶数
那么一定用1 * 2的多米诺骨牌可以覆盖
但是要求其中k个必须水平放置
输出能不能
输出方案

https://codeforces.com/contest/1551/problem/E
输入一个序列,从中删去一些数字,使得a[i] == i的位置最多
动态规划

https://codeforces.com/contest/1551/problem/F
输入一个大小为n的树,找出一个大小为k的集合,集合中的点距离都相同

CF1260C Infinite Fence

https://codeforces.com/problemset/problem/1260/C
https://www.luogu.com.cn/problem/CF1260C
输入R,G,K有无限个栏杆
要求R的倍数必须是红色
要求G的倍数必须是绿色
同时是R和G的倍数可以选择红色还是绿色
然后删去没染色的位置,问能不能不存在连续K个同色

CF1294B Collecting Packages

https://codeforces.com/problemset/problem/1294/B
https://www.luogu.com.cn/problem/CF1294B
n个坐标(x,y)
按x排序,问y是否有序
输出从(0,0)走到这n个点的方案
要求优先向右

CF1251C Minimize The Integer

https://codeforces.com/problemset/problem/1251/C
https://www.luogu.com.cn/problem/CF1251C
输入一个数字,你可以交换奇偶性不同的且相邻的两个数字
问结果最小是多少

CF1007A Reorder the Array

https://codeforces.com/problemset/problem/1007/A
https://www.luogu.com.cn/problem/CF1007A
他们正在一起建立一个序列。在开始的时候,这个序列是空的。
在一个回合中,玩家可以从数组的左边或右边移出一个数字,并将其添加到序列的右侧。
规则是:他们所建立的序列必须是单调递增的。赢家就是是做出最后一步的玩家。
Alice 是第一个玩的。假设他们都以最佳方式进行游戏的情况下,谁能赢得游戏?

CF721D Maxim and Array

https://codeforces.com/problemset/problem/721/D
https://www.luogu.com.cn/problem/CF721D
输入N个数字,最多执行K次操作,每次操作是选一个数字+X或者-X
希望操作之后所有数字乘积最小,问操作之后N个数字分别是什么

CF445B

https://codeforces.com/problemset/problem/445/B
https://www.luogu.com.cn/problem/CF445B

DZY热爱化学.他有n种物质,其中m对会反应
他把它们一种一种倒到烧杯里
有一个危险值,一开始等于1
如果一种物质倒到烧杯里之后至少有一种物质能和它反应,则危险值乘以二
否则危险值不变.求所有物质倒到烧杯里之后最大的危险值

每个化学物质看做一条边,每个连通块大小-1之和是答案

CF11D A Simple Task

https://codeforces.com/problemset/problem/11/D
输入一个无向图,问有多少个简单环
简单环要求至少3个点,并且不能有重复的点和重复的边

考虑到绝大多数 codeforces 题目在 luogu 上都有翻译,这个列表不会经常更新

[MUMETOC]

CF1669D Colorful Stamp

https://codeforces.com/problemset/problem/1669/D
初始一个全是W的字符串
每次操作可以选相邻两个位置,一个改成R,一个改成B
(选过的位置可以重复选)
(不能只选一个,必须是选相邻的2个位置)
(这两个位置可以改成BR也可以改成RB)
问最终结果是否可能是输入的字符串?

按W把字符串分成若干个非空段,如果每一段都有R且有B,那么就是YES
否则就是NO

CF1669E 2-Letter Strings

https://codeforces.com/problemset/problem/1669/E
输入n个长度为2的字符串
输出有多少对(s[i],s[j])字符串恰好差一个字符
s和t恰好差一个字符 的意思是 (s[0],t[0]) (s[1],t[1])两组位置,一组一样,另一组不一样
比如 AB 和 BC 不算差一个字符,因为两组位置都不一样

对是无序对,要求i<j

方法一:
对于每个种字符串,统计个数,一共至多有676种字符串
枚举676种字符串之间,哪些对字符串,恰好差一个字符

方法二:
每次读入一个新字符串s之后
答案 += 以s[0]开始的字符串个数 + 以s[1]结束的字符串个数 - 2 * 字符串s出现的次数

CF1660B Vlad and Candies

https://codeforces.com/problemset/problem/1660/B
多组数据
每组数据输入一个长度为n的数组
每次从数组中选一个最大值自减1
直到数组中所有数字变为0
不希望有一个数字连续两次减少1
问能不能做到,如果可以输出Yes,如果做不到输出No

https://codeforces.com/contest/1660/submission/151534158
特别注意n=1的情况,需要单独判断a[0]是不是1

CF1656A Good Pairs

https://codeforces.com/contest/1656/problem/A
输入一个数组,找到两个下标(i,j),使得对于所有的k都满足
abs(a[i] - a[k]) + abs(a[k] - a[j]) == abs(a[i] - a[j])

i是最大值的位置
j是最小值的位置

https://codeforces.com/contest/1656/submission/150990878

CF1656B Subtract Operation

https://codeforces.com/contest/1656/problem/B
输入一个数组a每次从中选择一个数字x,删去这个数字,其他数字都a[i]-=x
问操作n-1次之后,能不能恰好剩下k

如果可以,最后剩下的两个数字之差是k,这两个数字从开始的差就是k
相当于询问数组中是否有两个数字之差是k

https://codeforces.com/contest/1656/submission/150990342

CF1656C Make Equal With Mod

https://codeforces.com/contest/1656/problem/C
输入一个数组a,每次可以选一个数字x(x>=2),所有数字a[i]%=x
问能不能让所有数字都一样

如果输入的数字没有1,每次把最大的数字操作成0就可以了

如果输入的数字有1, 1是不会变的,每次把最大的数字操作成1就可以了

所以要求不能有相差为1的数字对

https://codeforces.com/contest/1656/submission/150989509

CF1656D K-good

https://codeforces.com/contest/1656/problem/D

输入 n 找 k(k>=2)
使得存在 k 个正整数,模 k 之后余数互不相同,并且这 k 个正整数的和是 n

相当于找k,使得 (n - k*(k+1)/2) % k == 0

如果k是奇数
那么 n % k == 0 && n >= k * (k + 1) / 2
满足k是奇数,且n%k==0的一个可行解是,n除以若干次2之后得到的结果

如果k是偶数
那么 n % (k/2) == 0 && n / (k/2) % 2 == 1 && n >= k * (k + 1) / 2
满足k是偶数,且n % (k/2) == 0 && n / (k/2) % 2 == 1,那么k一定是2的次幂

这两个解中,一定有一个k满足 n >= k * (k + 1) / 2

如果n是奇数 k=2
如果n是2的倍数 但不是4的倍数 k=4
如果n是4的倍数 但不是8的倍数 k=8

#include <bits/stdc++.h>
using namespace std;
int t;
long long n;
int main()
{
	cin >> t;
	for (int tt = 0; tt < t; tt++)
	{
		cin >> n;
		long long x = n & -n;
		if (x == n)
		{
			cout << -1 << endl;
		}
		else
		{
			// k = n / x;
			// k = 2 * x;
			// 较小的一定满足 k*(k+1)/2 <= n
			cout << min(n / x, 2 * x) << endl;
		}
	}
	return 0;
}

spoj IBONEC
矩阵乘法 f[n]=f[n-1]+f[n-2]+f[n-4]

spoj BOBAINV
逆序对 二维部分和

spoj OVICUMSUM
前缀和 FFT 组合数

luogu P5488
前缀和 FFT 组合数

spoj HMLIS
最长不下降子序列 线段树 方案数

spoj FIBHARD
Fibonacci数 循环节

spoj XORX
Trie树 最大异或

spoj RGBRED
简单贪心,分析奇偶

spoj SQRPERF
线性基

spoj EXPOR
位运算,unsigned long long

spoj GCDEASY
gcd,随便做

spoj YAXS
1 2 3 相互异或,1 4 5 相互异或

spoj SORTMUCH
整数二分

spoj IBIGAME
简单博弈

spoj ZING02
找规律特判,注意1的特例

spoj SWAPDIFF1
逆序对

spoj PROD1GCD
筛法,快速幂

spoj TPGA
康托展开

spoj 题解

HK

注意到 m 很小,直接 dp
和超过 m 的状态可以直接忽略

ARRTWIST

直接 Yes 很好的证明题
抽屉原理

luogu 题解

Leetcode 题解

Leetcode 有一些非常有特色的题目,其中比较有趣的题解我会记录下来

双向广搜
A*
欧拉回路
线性筛
组合数学
Trie
平衡树
点分治
2SAT

  1. 自我介绍
  2. 这个课讲什么
  3. 数学题和编程题对比
    1. 数学题
    2. 编程题
  4. 编程题的特点
  5. 竞赛的路径
    1. 中国的比赛
    2. 世界的比赛
    3. 各国的比赛
    4. 大学生的比赛
    5. 商业比赛
      1. 中国的
      2. 外国的
    6. 每周都会有的比赛
  6. 开始学习的时间点
  7. 我们这里的课程设置
  8. 为什么学编程
    1. 升学
    2. 找工作
  9. 怎么学编程
  10. 学习准备
  11. Python
    1. DEVCPP
      1. 安装
      2. 修改语言
  12. C++ 语法学习
    1. 第一个能编译的程序
    2. 输入输出
    3. C++的if
    4. 判断练习题
      1. abc191_a Vanishing Pitch
      2. abc176_a Takoyaki
      3. abc167_a Registration
      4. abc165_a We Love Golf
      5. abc158_a Station and Bus
      6. abc072_a Sandglass2
      7. abc059_a Three-letter acronym
      8. abc057_a Remaining Time
      9. abc056_a HonestOrDishonest
      10. abc055_a Restaurant
    5. 循环
      1. while
      2. for
      3. do while
      4. break 语句
      5. continue 语句
      6. goto 语句
      7. 无限循环
      8. 循环
    6. 多层循环
    7. 循环练习题
      1. abc215_b log2(N)
      2. abc207_b Hydrate
      3. abc206_b Savings
      4. abc204_b Nuts
      5. abc203_b AtCoder Condominium
      6. abc200_b 200th ABC-200
      7. abc193_b Play Snuke
      8. abc191_b
      9. abc190_b Magic 3
      10. abc189_b Alcoholic
    8. C++的数组
      1. 声明数组
    9. 常见问题
    10. 数组练习题
      1. abc161_b Popular Vote
    11. 数组或循环练习题
      1. abc151_b Achieve the Goal
      2. abc143_b TAKOYAKI FESTIVAL 2019
      3. abc142_b Roller Coaster
      4. abc135_b 0 or 1 Swap
      5. abc130_b Bounding
      6. abc127_b Algae
      7. abc124_b Great Ocean View
      8. abc120_b K-th Common Divisor
    12. 基础语法题
      1. abc070_b Two Switche
      2. abc134_b Golden Apple
      3. abc126_b YYMM or MMYY
      4. abc154_a Remaining Balls
      5. abc153_a Serval vs Monster
      6. abc148_a Round One
      7. abc144_a 9x9
      8. abc122_a Double Helix
      9. abc121_a White Cells
    13. 参考资料
  13. C++字符串
    1. 存储
    2. 读入
    3. 输出
    4. 字符串长度
    5. 字符串比较
    6. find
    7. string
      1. abc068_a ABCxxx
      2. abc132_a Fifty-Fifty
      3. abc146_a Can't Wait for Holiday
      4. abc166_a A?C
      5. abc175_a Rainy Season
      6. abc217_a Lexicographic Order
      7. abc218_a Weather Forecast
      8. abc179_a Plural Form
      9. abc224_a Tires
    8. 可以用string也可以用int
      1. abc073_a September 9
      2. abc162_a Lucky 7
      3. abc187_a Large Digits
    9. lower_bound / upper_bound
      1. unique
    10. next_permutation
      1. P1706 全排列问题
      2. abc221_c Select Mul
      3. abc150_c Count Order
      4. abc145_c Average Length
      5. abc215_c One More aab aba baa
      6. CF124B Permutations
  14. 排序
    1. 排序和离散化
      1. CF670C Cinema
      2. abc213_c Reorder Cards
      3. CF344A Magnets
      4. abc143_c Slimes
      5. abc164_c gacha
      6. abc221_d Online games
      7. vector / queue / stack / deque
    2. set 集合
      1. 支持的操作:
      2. 不支持的操作
    3. 枚举
    4. 加入
    5. 删除
    6. map
      1. Java
    7. 参考题目
      1. abc253_c
  15. C++ STL
    1. 简介
      1. vector / queue / stack / deque
      2. priority_queue
  16. include
    1. P1168 中位数
    2. set 集合
    1. 支持的操作:
    2. 不支持的操作
    3. 枚举
    4. 加入
    5. 删除
    6. map
    1. Java
    7. 参考题目
    1. abc253_c
    2. 题目
    3. sort unique lower_bound upper_bound
    8. 广度优先搜索 BFS
    1. 算法特点
    2. P1443
    3. P1747
    4. P1746
    5. P2298
    6. P1332
    7. P1825 [USACO11OPEN]Corn Maze S
    9. memset
  17. 差分前缀和
    1. 定义
    2. 高维情况
    3. 参考题目
    4. 参考资料
  18. 前缀和
    1. CF816B Karen and Coffee
    1. P1083 借教室
    2. P1314 聪明的质监员
    3. P1114 “非常男女”计划
    4. P3131 [USACO16JAN]Subsequences Summing to Sevens
    5. P1865 A % B Problem
    6. P1360 [USACO07MAR]Gold Balanced Lineup G
    7. P1387 最大正方形
    8. P2671 求和
    9. P3406 海底高铁
    10. P3662 [USACO17FEB]Why Did the Cow Cross the Road II S
    11. P4825 [USACO15FEB]Cow Hopscotch S
    12. P3173 [HAOI2009]巧克力
    13. P2879 [USACO07JAN]Tallest Cow S
    14. P3909 异或之积
    15. P1714 切蛋糕
    16. P2629 好消息,坏消息
    17. P4677 山区建小学
    18. P2280 [HNOI2003]激光炸弹
    19. P3819 松江1843路
    20. P4552 [Poetize6] IncDec Sequence
    21. spoj ADARAINB
  19. 高精度
    1. 简介
    2. 读入
    3. 加减
    4. 乘法
    5. 除法取模
    6. 进制转换
    7. 压位
    8. 其他语言
    9. 参考题目
  20. 排序
    1. 排序和离散化
      1. CF670C Cinema
      2. abc213_c Reorder Cards
      3. CF344A Magnets
      4. abc143_c Slimes
      5. abc164_c gacha
      6. abc221_d Online games
  21. 归并排序
    1. 逆序对
  22. 快速排序
    1. 排序
    2. O(n) 求第k大数
    3. 堆排序
    4. 结构体排序
      1. CF659B Qualifying Contest
      2. CF810B Summer sell-off
      3. CF976C Nested Segments
      4. CF597B Restaurant
      5. CF808C Tea Party
      6. CF230A Dragons
      7. abc128_b Guidebook
      8. abc116_d Various Sushi
    5. 进制转换
      1. abc220_b Base K
      2. abc156_b Digits
      3. abc105_c Base -2 Number
      4. CF1249C1 / CF1249C2 Good Numbers
      5. CF1110A Parity
      6. CF552C Vanya and Scales
      7. abc192_c Kaprekar Number
      8. P1143 进制转换
      9. P1017 [NOIP2000 提高组] 进制转换
      10. spoj THRPWRS
  23. 数学
    1. Pick's Theorem
      1. P2735 [USACO3.4]网 Electric Fences
      2. P1403 [AHOI2005]约数研究
      3. abc172_d
      4. P2926 [USACO08DEC]Patting Heads S
  24. include <bits/stdc++.h>
  25. 快速幂
    1. 问题
    2. 基本算法
    3. 代码
    4. 其他语言
    5. 推广
    6. 题目
    7. 参考资料
      1. spoj NDT
    8. 位运算
      1. 有符号数处理 (signed number representations)
      2. 取反 (bitwise NOT, complement)
      3. 位与 (bitwise AND)
      4. 位或 (bitwise OR)
      5. 位异或 (bitwise XOR)
      6. 左移
      7. 右移
      8. 内置函数
    9. 题目列表
      1. P1469 找筷子
      2. 取反!
      3. 逻辑与 && and
      4. 逻辑或 || or
      5. 二进制
      6. 按位取反 ~ compl
      7. 补码
      8. 按位与& bitand
      9. 按位或| bitor
      10. 异或^ xor
      11. 左移 <<
      12. 右移 >>
    10. 格雷码
  26. 质数
    1. 判断质数
    2. 筛法
  27. 高级数论
    1. Miller Robin
    2. Polland Rho
    3. 杜教筛
    4. Min_25 筛
  28. 矩阵乘法
    1. 简介
    2. 矩阵乘法
    3. Fibonacci数
    4. 参考题目
      1. P1349 广义斐波那契数列
      2. P1939 【模板】矩阵加速(数列)
      3. P1707 刷题比赛
      4. P2044 [NOI2012]随机数生成器
      5. P2461 [SDOI2008]递归数列
      6. P4910 帕秋莉的手环
      7. P1357 花园
      8. P5004 专心OI - 跳房子
      9. P3746 [六省联考2017]组合数问题
      10. P4599 [HEOI2012]赵州桥
      11. bzoj 3157: 国王奇遇记
  29. include <bits/stdc++.h>
  30. 乘法逆元
  31. 逆元
    1. 加法逆元
    2. 乘法逆元
    3. 如何求
      1. 快速幂
      2. 扩展欧几里得
      3. O(n)O(n)11nn的乘法逆元
    4. 参考题目
    5. 参考资料
  32. 复数
    1. 实数 虚数
  33. bitset
  34. spoj ADACOINS
    1. spoj ADAFUROW
    1. spoj ADACHERY
  35. 三角函数
    1. sin
    2. cos
    3. tan
    4. 正弦定理
    5. 余弦定理
    6. LGV 引理
      1. P6657 【模板】LGV 引理
  36. 卢卡斯定理
    1. 简介
    2. 代码
    3. 奇偶性
    4. 证明
    5. 勒让德定理
    6. 库默尔定理
    7. 参考题目
    8. 参考资料
  37. 叉积
    1. 二维情况
    2. 三维情况
      1. 多边形面积
    3. 匹克定理
    4. Convex Hull
      1. Cross Product
      2. Convex Hull
      3. Dynamic Covex Hull
      4. Volume of a Parallelepiped
  38. 图论
    1. 基本概念
      1. 二元组的定义
      2. 有向图和无向图
      3. 简单图
      4. 出度和入度
      5. 子图
      6. 补图
      7. 完全图
      8. 独立集
      9. 二分图
      10. 完全二分图
      11. 平面图
    2. NP完全
    3. 图的存储
    4. 图的遍历
    5. 拉姆齐定理
    6. 欧拉回路
    7. 参考题目
    8. 参考资料
    9. 图论 Graph Theory
      1. 存边
      2. 邻接链表 adjacent list
      3. 邻接矩阵 adjacent matrix
  39. 最短路 Shortest Path
    1. 图的存储
    2. 基本性质
    3. Bellman Ford
      1. Bellman Ford
      2. P1938 [USACO09NOV]Job Hunt S
  40. Dijkstra
    1. Dijkstra
    2. 参考题目
      1. spoj ADRABR
      2. P5201 [USACO19JAN]Shortcut G
      3. P5122 [USACO18DEC]Fine Dining G
      4. P1629 邮递员送信
      5. P4779 【模板】单源最短路径(标准版)
      6. bzoj 3040
    3. Floyd
      1. Floyd
      2. P2910 [USACO08OPEN]Clear And Present Danger S
      3. CF25C
    4. Floyd算法的应用
      1. P6175 无向图的最小环问题
      2. P1119 灾后重建
    5. 用 bitset 做传递闭包 use bitset to maintain
      1. P4306 [JSOI2010]连通数
  41. SPFA
    1. 队列优化版的Bellman Ford
      1. SPFA (Only in China, useless)
    2. SPFA
    3. 其他变形
    4. 参考题目
    5. 参考资料
      1. P2176
      2. P4568
      3. P2939 [USACO09FEB]Revamping Trails G
      4. P5837 [USACO19DEC]Milk Pumping G
      5. P1821 [USACO07FEB]Silver Cow Party S
      6. P2865 [USACO06NOV]Roadblocks G
      7. P3659 [USACO17FEB]Why Did the Cow Cross the Road I G
      8. CF938D Buy a Ticket
    6. BFS
    7. DAG中的最短路
      1. P6145
      2. CF666B World Tour
      3. CF986A Fair
  42. 差分约束
    1. 简介
    2. 判断有无解
    3. 求出一组解
    4. 参考题目
      1. P2939 [USACO09FEB]Revamping Trails G
    5. 阅读其他
  43. Dijkstra
    1. Dijkstra
    2. 参考题目
      1. spoj ADRABR
      2. P5201 [USACO19JAN]Shortcut G
      3. P5122 [USACO18DEC]Fine Dining G
      4. P1629 邮递员送信
      5. P4779 【模板】单源最短路径(标准版)
      6. bzoj 3040
  44. SPFA
    1. 队列优化版的Bellman Ford
      1. SPFA (Only in China, useless)
    2. SPFA
    3. 其他变形
    4. 参考题目
    5. 参考资料
    6. Floyd
      1. Floyd
      2. P2910 [USACO08OPEN]Clear And Present Danger S
      3. CF25C
    7. Floyd算法的应用
      1. P6175 无向图的最小环问题
      2. P1119 灾后重建
    8. 用 bitset 做传递闭包 use bitset to maintain
      1. P4306 [JSOI2010]连通数
  45. 最小生成树
    1. 性质
    2. Borůvka's algorithm
    3. 参考题目
    4. 参考资料
  46. 最小生成树 (Minimum Spanning Tree)
    1. Prim
    2. Prim's algorithm
    3. Kruskal's algorithm
    4. Kruskal
    5. Kruskal
    6. Prim
    7. Prim's algorithm
    8. Kruskal's algorithm
    1. 定义
    2. 性质
    3. 存储
    4. 树的遍历
    5. 森林
    6. 其他介绍
    7. 二叉树
    8. DFS/BFS
    9. LCA
    10. 参考题目
    11. 参考资料
    12. 树的直径
    13. 树的重心
  47. Lowest Common Ancestor 最近公共祖先
    1. 2 ways to brute forces, find LCA(x, y) 2种暴力方式
    2. 一些题目
    3. spoj POLICEMEN
  48. 拓扑排序
    1. 简介
    2. 简介
    3. BFS的写法
    4. DFS的写法
    5. 一个不太好做的问题
    6. 拓扑排序和动态规划
    7. 练习题
      1. abc223_d
      2. abc245_f Endless Walk
      3. abc216_d Pair of Balls
      4. icpc2012autumn_a Dictionary
      5. CF510C Fox And Names
      6. CF825E Minimal Labels
      7. CF883B Berland Army
      8. CF915D Almost Acyclic Graph
      9. CF1100E Andrew and Taxi
      10. CF1217D Coloring Edges
      11. P1038 神经网络
      12. P1347 排序
      13. P1983 车站分级
      14. P2017 [USACO09DEC]Dizzy Cows G
      15. P3183 [HAOI2016]食物链
      16. P3243 [HNOI2015]菜肴制作
      17. P4376 [USACO18OPEN]Milking Order G
      18. P6145 [USACO20FEB]Timeline G
      19. P5603 小C与桌游
  49. 动态规划
    1. 经典题目
      1. 数字三角形
      2. 最长不下降子序列
      3. 最长公共子序列
      4. 线性状态动态规划
      5. 区间动态规划
      6. 树形动态规划
      7. 背包
      8. 有向无环图动态规划 DAG
      9. 状态压缩动态规划
      10. 数位动态规划
      11. P1216 [USACO1.5][IOI1994]数字三角形 Number Triangles
      12. 记忆化搜索
      13. 背包
      14. P5156
  50. include <bits/stdc++.h>
  51. include <bits/stdc++.h>
    1. spoj REDRONESIA
    2. .#aaa.
    3. a.#.##
    4. b#.b..
    5. bb#b##
  52. include <bits/stdc++.h>
  53. include <bits/stdc++.h>
    1. spoj ISELECT
    1. spoj SNOWGAME
  54. include
  55. include
  56. include
  57. include
  58. include
  59. define mod 9999973
  60. define R register
  61. include<stdio.h>
  62. include
  63. include<string.h>
  64. define C(x) __builtin_popcount(x)
    1. 树形DP
  65. 树形DP
    1. 简介
    2. 常见技巧
    3. 参考题目
    4. 区间DP
      1. 循环顺序
      2. 相关题目
      3. P1880 [NOI1995] 石子合并
      4. P1220 关路灯
      5. P3205 [HNOI2010]合唱队
      6. P4170 [CQOI2007]涂色
      7. P3146 [USACO16OPEN]248 G
      8. abc252_g
  66. DAG上的DP
    1. P4017 最大食物链计数
    1. P4316 绿豆蛙的归宿
    2. P1434 [SHOI2002]滑雪
    3. P1434 [SHOI2002]滑雪
    4. P1464 Function
    5. Luogu P1807
    6. bzoj 3036
    7. P1807 最长路
    8. P1113 杂务
    9. P1137 旅行计划
  67. 背包
    1. 基本思路
    2. 零一背包
    3. 完全背包
    4. 多重背包
    5. 背包统计方案数
    6. bitset
    7. 参考题目
    8. 背包问题 knapsack problem
      1. 零一背包 完全背包 有限背包
      2. 统计方案数 求最优解
  68. include <bits/stdc++.h>
  69. include <bits/stdc++.h>
    1. 练习题
    1. spoj WEIGHT3
    2. spoj WEIGHT1
    3. 退背包
  70. 并查集
    1. 简介
    2. 时间复杂度
    3. 可持久化与撤销
    4. 参考题目
  71. include <bits/stdc++.h>
  72. include <bits/stdc++.h>
    1. spoj QN02
      1. priority_queue
  73. include
    1. P1168 中位数
  74. 树状数组
    1. 名称
    2. Lowbit
    3. 基本原理
    4. 基本操作
      1. 修改操作
      2. 查询操作
      3. kk大操作
      4. O(n)O(n)建树状数组
    5. 树状数组,STL set与平衡树
    6. 其他推广
    7. 高维推广 / 二维树状数组
    8. 参考题目
      1. 133. 二维树状数组 1:单点修改,区间查询
      2. 134. 二维树状数组 2:区间修改,单点查询
      3. 135. 二维树状数组 3:区间修改,区间查询
      4. P3960 [NOIP2017 提高组] 列队
      5. P3374 【模板】树状数组 1
      6. P3368 【模板】树状数组 2
      7. P2068 统计和
      8. P5057 [CQOI2006]简单题
      9. P5142 区间方差
      10. P3608 [USACO17JAN]Balanced Photo G
      11. P6278 [USACO20OPEN]Haircut G
      12. P6278 [USACO20OPEN]Haircut G
      13. P5200 [USACO19JAN]Sleepy Cow Sorting G
      14. P3660 [USACO17FEB]Why Did the Cow Cross the Road III G
      15. P3372 【模板】线段树 1
      16. P4868 Preprefix sum
      17. P3605 [USACO17JAN]Promotion Counting P
      18. P5094 [USACO04OPEN] MooFest G 加强版
      19. 数据范围
      20. poj 2828 Buy Tickets
      21. P2345 [USACO04OPEN]MooFest G
      22. P5142 区间方差
      23. poj 2155
      24. poj 1195 Mobile phones
      25. poj 2352 Stars
      26. P3605 [USACO17JAN]Promotion Counting P
      27. P4514 上帝造题的七分钟
      28. P2344 [USACO11FEB]Generic Cow Protests G
      29. P6098
      30. abc185_f
      31. CF1660F2
      32. spoj DEV_CP
  75. 线段树
    1. 实现
    2. 打标记
    3. 矩形面积并
    4. 可持久化线段树
    5. 树链剖分
    6. 统计的力量
    7. 和树状数组对比
    8. 参考题目
    9. 参考资料
  76. 线段树
    1. spoj SPAM_ATX
    2. 栈 (stack)
    1. CF81A Plug-in
    2. Luogu P1165 日志分析
    3. Luogu P1449 后缀表达式
    4. Luogu P5788 【模板】单调栈
    5. agc005_a STring
  77. Stack
    1. 名称
    2. 实现
    3. 具体使用
      1. 表达式求值
      2. 凸包/凸壳
      3. 单调栈
      4. 递归
    4. 参考题目
  78. 队列
    1. 名称
    2. 实现
    3. 应用
    4. 参考题目
  79. 队列 Queue
    1. BFS求最短路 BFS to shortest path
    2. 单调栈 Monotonic Stack
      1. 最大矩形
  80. 单调队列
    1. P1886 滑动窗口 /【模板】单调队列
      1. P3088 [USACO13NOV]Crowded Cows S
      2. P2627 / P2034
      3. P5202 [USACO19JAN]Redistricting P
      4. P3957 跳房子
    2. Monotonic Queue
  81. 单调队列
    1. 简介
    2. 算法流程
    3. 参考题目
    4. spoj FESTIVAL
    5. spoj ADASQR
    6. 参考资料
  82. 树链剖分
    1. 简单介绍
    2. 轻重链剖分
    3. 时间复杂度
    4. 参考题目
  83. 树链剖分
  84. ST
    1. 名称
    2. 区间最值
    3. 算法原理
    4. 时间复杂度
    5. 适用题目
    6. 其他应用
    7. 高维推广
    8. 参考代码
    9. 参考题目
  85. RMQ
    1. 区间最值
    2. Sparse Table
    3. 线段树
    4. 笛卡尔树
    5. 理论最优
    6. 参考题目
    7. 参考资料
  86. splay
  87. treap
  88. 可并堆
    1. 简介
    2. 左偏树
      1. 其他操作
    3. 随机合并
    4. 启发式合并
    5. 参考题目
    6. 参考资料
  89. pbds
    1. 简介
  90. DFS序
    1. 二叉树的DFS序
    2. 进出栈序
    3. 欧拉序
    4. 参考题目
    5. 字符串
  91. AC自动机 (Aho–Corasick algorithm)
    1. 如何建树
    2. Fail Tree
    3. AC自动机 / TRIE图
    4. 参考题目
      1. P3796 【模板】AC自动机(加强版)
      2. P3808 【模板】AC自动机(简单版)
      3. P5357 【模板】AC自动机(二次加强版)
      4. P4052 [JSOI2007]文本生成器
      5. P3041 [USACO12JAN]Video Game G
      6. P5840 [COCI2015]Divljak
      7. P3311 [SDOI2014] 数数
      8. P2536 [AHOI2005]病毒检测
      9. P3121 [USACO15FEB]Censoring G
      10. P2414 [NOI2011] 阿狸的打字机
      11. P3966 [TJOI2013]单词
      12. P5231 [JSOI2012]玄武密码
      13. P3167 [CQOI2014]通配符匹配
      14. P2336 [SCOI2012]喵星球上的点名
      15. P2603 [ZJOI2008]无序运动
      16. P3715 [BJOI2017]魔法咒语
      17. P2292 [HNOI2004]L语言
      18. P4045 [JSOI2009]密码
  92. KMP
    1. 参考代码
    1. P4391
    2. P3435
    3. hdu 1711 Number Sequence
    4. hdu 2087 剪花布条
    5. hdu 2594 Simpsons’ Hidden Talents
    6. poj 3461 Oulipo
    7. poj 2406 Power Strings
    8. arc060_d
  93. 后缀数组
    1. height 数组
  94. AC自动机 (Aho–Corasick algorithm)
    1. 如何建树
    2. Fail Tree
    3. AC自动机 / TRIE图
    4. 参考题目
      1. P3796 【模板】AC自动机(加强版)
      2. P3808 【模板】AC自动机(简单版)
      3. P5357 【模板】AC自动机(二次加强版)
      4. P4052 [JSOI2007]文本生成器
      5. P3041 [USACO12JAN]Video Game G
      6. P5840 [COCI2015]Divljak
      7. P3311 [SDOI2014] 数数
      8. P2536 [AHOI2005]病毒检测
      9. P3121 [USACO15FEB]Censoring G
      10. P2414 [NOI2011] 阿狸的打字机
      11. P3966 [TJOI2013]单词
      12. P5231 [JSOI2012]玄武密码
      13. P3167 [CQOI2014]通配符匹配
      14. P2336 [SCOI2012]喵星球上的点名
      15. P2603 [ZJOI2008]无序运动
      16. P3715 [BJOI2017]魔法咒语
      17. P2292 [HNOI2004]L语言
      18. P4045 [JSOI2009]密码
  95. 自动机
    1. 子序列自动机
    2. AC自动机
    3. 后缀自动机
      1. abc254_a Last Two Digits
      2. abc254_b Practical Computing
      3. abc254_c K Swap
      4. abc254_d
      5. abc254_e
      6. abc254_f
      7. abc253_a Median?
      8. abc253_b Distance Between Tokens
      9. abc253_c Max - Min Query
      10. abc253_d FizzBuzz Sum Hard
      11. abc253_e Distance Sequence
      12. abc253_f Operations on a Matrix
      13. abc253_g Swap Many Times
      14. abc252_a
      15. abc252_b
      16. abc252_c
      17. abc252_d
      18. abc252_e
      19. abc252_f
      20. abc252_g
      21. abc252_e
      22. abc251_a
      23. abc251_b
      24. abc251_c
      25. abc251_d
      26. abc251_e
      27. abc251_f
      28. abc250_a Adjacent Squares
      29. abc250_b Enlarged Checker Board
      30. abc250_c Adjacent Swaps
      31. abc250_d 250-like Number
      32. abc250_e Prefix Equality
      33. abc249_a Jogging
      34. abc249_b Perfect String
      35. abc249_c Just K
      36. abc249_d Index Trio
      37. abc249_e RLE
      38. abc248_a Lacked Number
      39. abc248_b Slimes
      40. abc248_c Dice Sum
      41. abc248_d Range Count Query
      42. abc248_e K-colinear Line
      43. abc248_f Keep Connect
      44. abc247_a Move Right
      45. abc247_b Unique Nicknames
      46. abc247_c 1 2 1 3 1 2 1
      47. abc247_d Cylinder
      48. abc247_e Max Min
      49. abc247_f Cards
      50. abc246_a Four Points
      51. abc246_b Get Closer
      52. abc246_c Coupon
      53. abc246_d 2-variable Function
      54. abc246_e Bishop 2
      55. abc246_f typewriter
      56. abc245_a Good morning
      57. abc245_b Mex
      58. abc245_c Choose Elements
      59. abc245_d Polynomial division
      60. abc245_e Wrapping Chocolate
      61. abc245_f Endless Walk
      62. abc244_a Last Letter
      63. abc244_b Go Straight and Turn Right
      64. abc244_c Yamanote Line Game
      65. abc244_d Swap Hats
      66. abc244_e King Bombee
      67. abc244_f Shortest Good Path
      68. abc243_a Shampoo
      69. abc243_b Hit and Blow
      70. abc243_c Collision 2
      71. abc243_d Moves on Binary Tree
      72. abc243_e Edge Deletion
      73. abc243_f Lottery
      74. abc243_g Sqrt
      75. abc242_a T-shirt
      76. abc242_b Minimize Ordering
      77. abc242_c 1111gal password
      78. abc242_d ABC Transform
      79. abc242_e (∀x∀)
      80. abc242_f Black and White Rooks
      81. abc241_a Digit Machine
      82. abc241_c Connect 6
      83. abc241_d Sequence Query
      84. abc241_e Putting Candies
      85. abc240_a Edge Checker
      86. abc240_c Jumping Takahashi
      87. abc240_d Strange Balls
      88. abc239_a Horizon
      89. abc239_b Integer Division
      90. abc239_c Knight Fork
      91. abc239_d Prime Sum Game
      92. abc238_a Exponential or Quadratic
      93. abc238_b Pizza
      94. abc238_c digitnum
      95. abc238_d AND and SUM
      96. abc238_e Range Sums
      97. abc238_f Two Exams
      98. abc237_a Not Overflow
      99. abc237_b Matrix Transposition
      100. abc237_c kasaka
      101. abc237_d LR insertion
      102. abc237_e Skiing
      103. abc236_a chukodai
      104. abc236_b Who is missing?
      105. abc236_c Route Map
      106. abc236_d Dance
      107. abc236_e Average and Median
      108. abc236_f Spices
      109. abc235_a Rotate
      110. abc235_b Climbing Takahashi
      111. abc235_c The Kth Time Query
      112. abc235_d Multiply and Rotate
      113. abc235_e MST + 1
      114. abc235_f Variety of Digits
      115. abc235_g Gardens
      116. abc234_a Weird Function
      117. abc234_b Longest Segment
      118. abc234_c Happy New Year!
      119. abc234_d Prefix K-th Max
      120. abc234_e Arithmetic Number
      121. abc234_f Reordering
      122. abc233_a 10yen Stamp
      123. abc233_b A Reverse
      124. abc233_c Product
      125. abc233_d Count Interval
      126. abc233_e Σ[k=0..10^100]floor(X/10^k)
      127. abc233_f Swap and Sort
      128. abc232_a QQ solver
      129. abc232_b Caesar Cipher
      130. abc232_c Graph Isomorphism
      131. abc232_d Weak Takahashi
      132. abc232_e Rook Path
      133. abc232_f Simple Operations on Sequence
      134. abc232_h King's Tour
      135. abc231_a Water Pressure
      136. abc231_b Election
      137. abc231_c Counting 2
      138. abc231_d Neighbors
      139. abc231_e Minimal payments
      140. abc230_a AtCoder Quiz 3
      141. abc230_b Triple Metre
      142. abc230_c X drawing
      143. abc230_d Destroyer Takahashi
      144. abc230_e Fraction Floor Sum
      145. abc229_a First Grid
      146. abc229_b Hard Calculation
      147. abc229_c Cheese
      148. abc228_a On and Off
      149. abc228_b Takahashi's Secret
      150. abc228_c Final Day
      151. abc227_a Last Card
      152. abc227_b KEYENCE building
      153. abc227_c ABC conjecture
      154. abc227_d Project Planning
      155. abc226_a Round decimals
      156. abc226_b Counting Arrays
      157. abc226_c Martial artist
      158. abc226_d Teleportation
      159. abc226_e Just one
      160. abc225_a Distinct Strings
      161. abc225_b Star or Not
      162. abc225_c Calendar Validator
      163. abc225_d Play Train
      164. abc224_a Tires
      165. abc224_b Mongeness
      166. abc224_c Triangle?
      167. abc223_a Exact Price
      168. abc223_b String Shifting
      169. abc223_h Xor Query
      170. abc222_a Four Digits
      171. abc222_b Failing Grade
      172. abc222_g 222
      173. abc221_a Seismic magnitude scales
      174. abc221_b typo
      175. abc221_h Count Multiset
      176. abc220_a Find Multiple
      177. abc220_b Base K
      178. abc219_a AtCoder Quiz 2
      179. abc219_b Maritozzo
      180. abc218_a Weather Forecast
      181. abc218_b qwerty
      182. abc217_a Lexicographic Order
      183. abc217_b AtCoder Quiz
      184. abc216_a Signed Difficulty
      185. abc216_b Same Name
      186. abc215_a Your First Judge
      187. abc215_b log2(N)
      188. abc215_d Coprime 2
      189. abc214_a New Generation ABC
      190. abc214_b How many?
      191. abc214_d Sum of Maximum Weights
      192. abc213_a Bitwise Exclusive Or
      193. abc213_b Booby Prize
      194. abc213_d Takahashi Tour
      195. abc212_a Alloy
      196. abc212_b Weak Password
      197. abc211_a Blood Pressure
      198. abc211_b Cycle Hit
      199. abc211_c chokudai
      200. abc211_d Number of Shortest paths
      201. abc211_e
      202. abc211_f
      203. abc210_a Cabbages
      204. abc210_b Bouzu Mekuri
      205. abc210_c Colorful Candies
      206. abc210_e Ring MST
      207. abc209_a Counting
      208. abc209_b Can you buy them all?
      209. abc209_c Not Equal
      210. abc208_a Rolling Dice
      211. abc208_b Factorial Yen Coin
      212. abc207_a Repression
      213. abc207_b Hydrate
      214. abc206_a Maxi-Buying
      215. abc206_b Savings
      216. abc206_c Swappable
      217. abc206_e Divide Both
      218. abc205_a kcal
      219. abc205_b Permutation Check
      220. abc204_a Rock-paper-scissors
      221. abc204_b Nuts
      222. abc204_c Tour
      223. abc204_d Cooking
      224. abc204_e Rush Hour 2
      225. abc204_f Hanjo 2
      226. abc203_a Chinchirorin
      227. abc203_b AtCoder Condominium
      228. abc202_a Three Dice
      229. abc202_b 180°
      230. abc201_a Tiny Arithmetic Sequence
      231. abc201_b Do you know the second highest mountain?
      232. abc200_a Century
      233. abc200_b 200th ABC-200
      234. abc199_a Square Inequality
      235. abc199_b Intersection
      236. abc199_c IPFL
      237. abc199_e Permutation
      238. abc198_a Div
      239. abc198_b Palindrome with leading zeros
      240. abc197_a Rotate
      241. abc197_b Visibility
      242. abc196_a Difference Max
      243. abc196_b Round Down
      244. abc195_a Health M Death
      245. abc195_b Many Oranges
      246. abc194_a I Scream
      247. abc194_b Job Assignment
      248. abc193_a Discount
      249. abc193_b Play Snuke
      250. abc193_c Unexpressed
      251. abc192_a Star
      252. abc192_b uNrEaDaBlE sTrInG
      253. abc191_a Vanishing Pitch
      254. abc191_b Remove It
      255. abc190_a Very Very Primitive Game
      256. abc190_b Magic 3
      257. abc190_c Bowls and Dishes
      258. abc189_a Slot
      259. abc189_b Alcoholic
      260. abc189_c Mandarin Orange
      261. abc188_a Three-Point Shot
      262. abc188_b Orthogonality
      263. abc188_c ABC Tournament
      264. abc188_d Snuke Prime
      265. abc187_a Large Digits
      266. abc187_b Gentle Pairs
      267. abc186_a Brick
      268. abc186_b Blocks on Grid
      269. abc186_c Unlucky 7
      270. abc186_d Sum of difference
      271. abc185_a ABC Preparation
      272. abc185_b Smartphone Addiction
      273. abc185_c Duodecim Ferra
      274. abc184_a Determinant
      275. abc184_b Quizzes
      276. abc184_c Super Ryuma
      277. abc184_d increment of coins
      278. abc183_a ReLU
      279. abc183_b Billiards
      280. abc183_c Travel
      281. abc182_a twiblr
      282. abc182_b Almost GCD
      283. abc182_c To 3
      284. abc181_a Heavy Rotation
      285. abc181_b Trapezoid Sum
      286. abc181_c Collinearity
      287. abc180_a box
      288. abc180_b Various distances
      289. abc180_c Cream puff
      290. abc179_a Plural Form
      291. abc179_b Go to Jail
      292. abc179_c A x B + C
      293. abc178_a Not
      294. abc178_b Product Max
      295. abc177_a Don't be late
      296. abc177_b Substring
      297. abc177_c Sum of product of pairs
      298. abc177_d Friends
      299. abc177_e Coprime
      300. abc176_a Takoyaki
      301. abc176_b Multiple of 9
      302. abc176_c Step
      303. abc175_a Rainy Season
      304. abc175_b Making Triangle
      305. abc175_c Walking Takahashi
      306. abc174_a Air Conditioner
      307. abc174_b Distance
      308. abc174_c Repsept
      309. abc174_d Alter Altar
      310. abc173_a Payment
      311. abc173_b Judge Status Summary
      312. abc172_a Calc
      313. abc172_b Minor Change
      314. abc172_d Sum of Divisors
      315. abc171_a αlphabet
      316. abc171_b Mix Juice
      317. abc171_c One Quadrillion and One Dalmatians
      318. abc171_d Replacing
      319. abc170_a Five Variables
      320. abc170_b Crane and Turtle
      321. abc170_c Forbidden List
      322. abc169_a Multiplication 1
      323. abc169_b Multiplication 2
      324. abc169_c Multiplication 3
      325. abc168_a ∴ (Therefore)
      326. abc168_b ... (Triple Dots)
      327. abc167_a Registration
      328. abc167_b Easy Linear Programming
      329. abc166_a A?C
      330. abc166_b Trick or Treat
      331. abc165_a We Love Golf
      332. abc165_b 1%
      333. abc164_a Sheep and Wolves
      334. abc164_b Battle
      335. abc163_a Circle Pond
      336. abc163_b Homework
      337. abc163_c management
      338. abc162_a Lucky 7
      339. abc162_b FizzBuzz Sum
      340. abc162_c FizzBuzz Sum
      341. abc161_a ABC Swap
      342. abc161_b Popular Vote
      343. abc161_c Replacing Integer
      344. abc160_a Coffee
      345. abc160_b Golden Coins
      346. abc160_c Traveling Salesman around Lake
      347. abc159_a The Number of Even Pairs
      348. abc159_b String Palindrome
      349. abc159_c Maximum Volume
      350. abc158_a Station and Bus
      351. abc158_b Count Balls
      352. abc158_c Tax Increase
      353. abc157_a Duplex Printing
      354. abc157_b Bingo
      355. abc156_a Beginner
      356. abc156_b Digits
      357. abc156_c Rally
      358. abc155_a Poor
      359. abc155_b Papers, Please
      360. abc155_c Poll
      361. abc155_d Pairs
      362. abc154_a Remaining Balls
      363. abc154_b I miss you...
      364. abc154_d Dice in Line
      365. abc153_a Serval vs Monster
      366. abc153_b Common Raccoon vs Monster
      367. abc153_c Fennec vs Monster
      368. abc153_d Caracal vs Monster
      369. abc152_a AC or WA
      370. abc152_b Comparing Strings
      371. abc152_c Low Elements
      372. abc151_a Next Alphabet
      373. abc151_b Achieve the Goal
      374. abc150_a 500 Yen Coins
      375. abc150_b Count ABC
      376. abc149_a Strings
      377. abc149_b Greedy Takahashi
      378. abc149_c Next Prime
      379. abc148_a Round One
      380. abc148_b Strings with the Same Length
      381. abc148_d Brick Break
      382. abc147_a Blackjack
      383. abc147_b Palindrome-philia
      384. abc147_c HonestOrUnkind2
      385. abc147_d Xor Sum 4
      386. abc146_a Can't Wait for Holiday
      387. abc146_b ROT N
      388. abc146_c Buy an Integer
      389. abc146_d Coloring Edges on Tree
      390. abc145_a Circle
      391. abc145_b Echo
      392. abc145_d Knight
      393. abc144_a 9x9
      394. abc144_b 81
      395. abc144_c Walk on Multiplication Table
      396. abc143_a Curtain
      397. abc143_b TAKOYAKI FESTIVAL 2019
      398. abc142_a Odds of Oddness
      399. abc142_b Roller Coaster
      400. abc142_d Disjoint Set of Common Divisors
      401. abc141_a Weather Prediction
      402. abc141_b Tap Dance
      403. abc140_a Password
      404. abc140_b Buffet
      405. abc140_c Maximal Value
      406. abc139_a Tenki
      407. abc139_b Power Socket
      408. abc139_d ModSum
      409. abc138_a Red or Not
      410. abc138_b Resistors in Parallel
      411. abc138_c Alchemist
      412. abc137_a +-x
      413. abc137_b One Clue
      414. abc137_c Green Bin
      415. abc136_a Transfer
      416. abc136_b Uneven Numbers
      417. abc136_c Build Stairs
      418. abc135_a Harmony
      419. abc135_b 0 or 1 Swap
      420. abc134_a Dodecagon
      421. abc134_b Golden Apple
      422. abc134_c Exception Handling
      423. abc134_e Sequence Decomposing
      424. abc133_a T or T
      425. abc133_b Good Distance
      426. abc133_c Remainder Minimization 2019
      427. abc132_a Fifty-Fifty
      428. abc132_b Ordinary Number
      429. abc132_c Divide the Problems
      430. abc131_a Security
      431. abc131_b Bite Eating
      432. abc131_c Anti-Division
      433. abc130_a Rounding
      434. abc130_b Bounding
      435. abc129_a Airplane
      436. abc129_b Balance
      437. abc128_a Apple Pie
      438. abc128_b Guidebook
      439. abc127_a Ferris Wheel
      440. abc127_b Algae
      441. abc127_c Prison
      442. abc126_a Changing a Character
      443. abc126_b YYMM or MMYY
      444. abc126_c Dice and Coin
      445. abc125_a Biscuit Generator
      446. abc125_b Resale
      447. abc125_c GCD on Blackboard
      448. abc124_a Buttons
      449. abc124_b Great Ocean View
      450. abc123_a Five Antennas
      451. abc123_b Five Dishes
      452. abc122_a Double Helix
      453. abc122_b ATCoder
      454. abc122_c GeT AC
      455. abc121_a White Cells
      456. abc121_b Can you solve this?
      457. abc120_a Favorite Sound
      458. abc120_b K-th Common Divisor
      459. abc120_c Unification
      460. abc120_d Decayed Bridges
      461. abc119_a Still TBD
      462. abc119_b Digital Gifts
      463. abc118_a B +/- A
      464. abc118_b Foods Loved by Everyone
      465. abc117_a Entrance Examination
      466. abc117_b Polygon
      467. abc116_a Right Triangle
      468. abc116_b Collatz Problem
      469. abc116_c Grand Garden
      470. abc115_a Christmas Eve Eve Eve
      471. abc115_b Christmas Eve Eve
      472. abc114_a 753
      473. abc114_b 754
      474. abc114_c 755
      475. abc114_c 756
      476. abc113_a Discount Fare
      477. abc113_b Palace
      478. abc112_a Programming Education
      479. abc112_b Time Limit Exceeded
      480. abc111_a AtCoder Beginner Contest 999
      481. abc111_b AtCoder Beginner Contest 111
      482. abc110_a Maximize the Formula
      483. abc110_b 1 Dimensional World's Tale
      484. abc110_d Factorization
      485. abc109_a ABC333
      486. abc109_b Shiritori
      487. abc109_c Skip
      488. abc108_a Pair
      489. abc108_b Ruined Square
      490. abc107_a Train
      491. abc107_b Grid Compression
      492. abc106_a Garden
      493. abc106_b 105
      494. abc106_c To Infinity
      495. abc105_a AtCoder Crackers
      496. abc105_b Cakes and Donuts
      497. abc104_a Rated for Me
      498. abc104_b AcCepted
      499. abc103_a Task Scheduling Problem
      500. abc103_b String Rotation
      501. abc102_a Multiple of 2 and N
      502. abc102_b Maximum Difference
      503. abc101_a Eating Symbols Easy
      504. abc101_b Digit Sums
      505. abc100_a Happy Birthday!
      506. abc100_b Ringo's Favorite Numbers
      507. abc099_a ABD
      508. abc099_b Stone Monument
      509. abc099_c Strange Bank
      510. abc098_a Add Sub Mul
      511. abc098_b Cut and Count
      512. abc097_a Colorful Transceivers
      513. abc097_b Exponential
      514. abc096_a Day of Takahashi
      515. abc096_b Maximum Sum
      516. abc096_c Grid Repainting 2
      517. abc096_d Five, Five Everywhere
      518. abc095_a Something on It
      519. abc095_b Bitter Alchemy
      520. abc094_a Cats and Dogs
      521. abc094_b Toll Gates
      522. abc093_a abc of ABC
      523. abc093_b Small and Large Integers
      524. abc092_a Traveling Budget
      525. abc092_b Chocolate
      526. abc091_a Two Coins
      527. abc091_b Two Colors Card Game
      528. abc090_a Diagonal String
      529. abc090_b Palindromic Numbers
      530. abc089_a Grouping 2
      531. abc089_b Hina Arare
      532. abc088_a Infinite Coins
      533. abc088_b Card Game for Two
      534. abc087_a Buying Sweets
      535. abc087_b Coins
      536. abc086_a Product
      537. abc086_b 1 21
      538. abc085_a Already 2018
      539. abc085_b Kagami Mochi
      540. abc085_c Otoshidama
      541. abc084_a New Year
      542. abc084_b Postal Code
      543. abc083_a Libra
      544. abc083_b Some Sums
      545. abc082_a Round Up the Mean
      546. abc082_b Two Anagrams
      547. abc081_a Placing Marbles
      548. abc081_b Shift only
      549. abc080_a Parking
      550. abc080_b Harshad Number
      551. abc079_a Good Integer
      552. abc079_b Lucas Number
      553. abc078_a HEX
      554. abc078_b ISU
      555. abc077_a Rotation
      556. abc077_b Around Square
      557. abc076_a Rating Goal
      558. abc076_b Addition and Multiplication
      559. abc075_a One out of Three
      560. abc075_b Minesweeper
      561. abc074_a Bichrome Cells
      562. abc074_b Collecting Balls (Easy Version)
      563. abc073_a September 9
      564. abc073_b Trapezoid Sum
      565. abc073_b Theater
      566. abc073_c Write and Erase
      567. abc072_a Sandglass2
      568. abc071_b Not Found
      569. abc071_a Meal Delivery
      570. abc070_a Palindromic Number
      571. abc070_b Two Switche
      572. abc069_a K-City
      573. abc069_b i18n
      574. abc068_a ABCxxx
      575. abc068_b Break Number
      576. abc067_a Sharing Cookies
      577. abc067_b Snake Toy
      578. abc066_a ringring
      579. abc066_b ss
      580. abc065_a Expired?
      581. abc065_b Trained?
      582. abc064_a RGB Cards
      583. abc064_b Traveling AtCoDeer Problem
      584. abc063_a Restricted
      585. abc063_b Varied
      586. abc062_a Grouping
      587. abc062_b Picture Frame
      588. abc061_a Between Two Integers
      589. abc061_b Counting Roads
      590. abc060_a Shiritori
      591. abc060_b Choose Integers
      592. abc059_a Three-letter acronym
      593. abc059_b Comparison
      594. abc058_a ι⊥l
      595. abc058_b ∵∴∵
      596. abc057_a Remaining Time
      597. abc057_b Checkpoints
      598. abc056_a HonestOrDishonest
      599. abc056_b NarrowRectanglesEasy
      600. abc055_a Restaurant
      601. abc055_b Training Camp
      602. abc054_a One Card Poker
      603. abc054_b Template Matching
      604. abc053_a ABC/ARC
      605. abc053_b A to Z String
      606. abc052_a Two Rectangles
      607. abc052_b Increment Decrement
      608. abc051_a Haiku
      609. abc051_b Sum of Three Integers
      610. abc050_a Addition and Subtraction Easy
      611. abc050_b Contest with Drinks Easy
      612. abc049_a UOIAUAI
      613. abc049_b Thin
      614. abc048_a AtCoder *** Contest
      615. abc048_b Between a and b ...
      616. abc047_a Fighting over Candies
      617. abc047_b Snuke's Coloring 2 (ABC Edit)
      618. abc046_a AtCoDeer and Paint Cans
      619. abc046_b Painting Balls with AtCoDeer
      620. abc045_a Trapezoids
      621. abc045_b Card Game for Three (ABC Edit)
      622. abc044_a Tak and Hotels (ABC Edit)
      623. abc044_b Beautiful Strings
      624. abc043_a Children and Candies (ABC Edit)
      625. abc043_b Unhappy Hacking (ABC Edit)
      626. abc042_a Iroha and Haiku
      627. abc042_b Iroha Loves Strings (ABC Edition)
      628. abc041_a 添字
      629. abc041_b 直方体
      630. abc041_d 徒競走
      631. abc040_a 赤赤赤赤青
      632. abc040_b □□□□□
      633. abc040_c
      634. abc039_a 高橋直体
      635. abc039_b エージェント高橋君
      636. abc039_c
      637. abc039_d
      638. abc038_a お茶
      639. abc038_b ディスプレイ
      640. abc037_a 饅頭
      641. abc037_b 編集
      642. abc036_a お茶
      643. abc036_b 回転
      644. abc035_a テレビ
      645. abc035_b ドローン
      646. abc034_a テスト
      647. abc034_b ペア
      648. abc033_a 暗証番号
      649. abc033_b 町の合併
      650. abc032_a 高橋君と青木君の好きな数
      651. abc032_b 高橋君とパスワード
      652. abc031_a ゲーム
      653. abc031_b 運動管理
      654. abc030_a 勝率計算
      655. abc030_b 時計盤
      656. abc029_a 複数形
      657. abc029_b カキ
      658. abc028_a テスト評価
      659. abc028_b 文字数カウント
      660. abc027_a 長方形
      661. abc027_b 島と橋
      662. abc026_a 掛け算の最大値
      663. abc026_b N重丸
      664. abc025_a 25個の文字列
      665. abc025_b 双子とスイカ割り
      666. abc024_a 動物園
      667. abc024_b 自動ドア
      668. abc023_a 加算王
      669. abc023_b 手芸王
      670. abc022_a Best Body
      671. abc022_b Bumble Bee
      672. abc022_c
      673. abc021_a 足し算
      674. abc021_b 嘘つきの高橋くん
      675. abc020_a クイズ
      676. abc020_b 足し算
      677. abc019_1 高橋くんと年齢
      678. abc019_2 高橋くんと文字列圧縮
      679. abc019_3 高橋くんと魔法の箱
      680. abc018_1 豆まき
      681. abc018_2 文字列の反転
      682. abc017_1 プロコン
      683. abc017_2 choku語
      684. abc016_1 12月6日
      685. abc016_2 A±B Problem
      686. abc015_1 高橋くんの研修
      687. abc015_2 高橋くんの集計
      688. abc014_1 けんしょう先生のお菓子配り
      689. abc014_2 価格の合計
      690. abc014_3
      691. abc013_1 A
      692. abc013_2 錠
      693. abc012_1 スワップ
      694. abc012_2 入浴時間
      695. abc011_1 来月は何月?
      696. abc011_2 名前の確認
      697. abc010_1 ハンドルネーム
      698. abc010_2 花占い
      699. abc009_1 引越し作業
      700. abc009_2 心配性な富豪、ファミリーレストランに行く。
      701. abc008_1 アルバム
      702. abc008_2 投票
      703. abc007_1 植木算
      704. abc007_2 辞書式順序
      705. abc006_1 世界のFizzBuzz
      706. abc006_2 トリボナッチ数列
      707. abc005_1 おいしいたこ焼きの作り方
      708. abc005_2 おいしいたこ焼きの食べ方
      709. abc004_1 流行
      710. abc004_2 回転
      711. abc003_1 AtCoder社の給料
      712. abc003_2 AtCoderトランプ
      713. abc002_1 正直者
      714. abc002_2 罠
      715. abc001_1 積雪深差
      716. abc001_2 視程の通報
      717. abc001_3 風力観測
      718. arc138_a Larger Score
      719. arc138_b 01 Generation
      720. arc137_a Coprime Pair
      721. arc137_b Count 1's
      722. arc137_c Distinct Numbers
      723. arc136_a A ↔ BB
      724. arc131_a Two Lucky Numbers
      725. arc131_b Grid Repainting 4
      726. arc131_c Zero XOR
      727. arc130_a Remove One Character
      728. arc130_b Colorful Lines
      729. arc128_a Gold and Silver
      730. arc127_a Leading 1s
      731. arc126_a Make 10
      732. arc113_a A*B*C
      733. arc110_a Redundant Redundancy
      734. arc108_a Sum and Product
      735. arc106_a 106
      736. arc085_a HSI
      737. arc077_a pushpush
      738. arc077_b 11
      739. arc067_a Factors of Factorial
      740. arc065_b Connectivity
      741. agc054_a Remove Substrings
      742. agc041_a Table Tennis Training
      743. agc040_a ><
      744. agc013_a Sorted Arrays
      745. agc010_a Addition
      746. zone2021_a UFO Invasion
      747. zone2021_b Sign of Friendship
      748. zone2021_c MAD TEAM
      749. hhkb2020_a Keyboard
      750. hhkb2020_b Futon
      751. hhkb2020_c Neq Min
      752. abl_a Repeat ACL
      753. abl_b Integer Preference
      754. abl_c Connect Cities
      755. panasonic2020_a Kth Term
      756. panasonic2020_b Bishop
      757. panasonic2020_c Sqrt Inequality
      758. aising2019_a Bulletin Board
      759. aising2019_b Contests
      760. soundhound2018_summer_qual_a F
      761. soundhound2018_summer_qual_b
      762. keyence2021_a Two Sequences 2
      763. tokiomarine2020_a Nickname
      764. nomura2020_a Study Scheduling
      765. nomura2020_b Postdocs
      766. hitachi2020_a Hitachi String
      767. hitachi2020_b Nice Shopping
      768. keyence2020_a Painting
      769. dwacon6th_prelims_a Falling Asleep
      770. ddcc2020_qual_a DDCC Finals
      771. ddcc2020_qual_b Iron Bar Cutting
      772. nikkei2019_2_qual_a Sum of Two Integers
      773. jsc2019_qual_a Takahashi Calendar
      774. diverta2019_2_a Ball Distribution
      775. m_solutions2019_a Sum of Interior Angles
      776. m_solutions2019_b Sumo
      777. diverta2019_a Consecutive Integers
      778. exawizards2019_a Regular Triangle
      779. exawizards2019_b Red or Blue
      780. yahoo_procon2019_qual_a Anti-Adjacency
      781. yahoo_procon2019_qual_b Path
      782. nikkei2019_qual_a Subscribers
      783. nikkei2019_qual_b Touitsu
      784. keyence2019_a Beginning
      785. dwacon5th_prelims_a Thumbnail
      786. exawizards2019_a Regular Triangle
      787. exawizards2019_b Red or Blue
      788. yahoo_procon2019_qual_a Anti-Adjacency
      789. yahoo_procon2019_qual_b Path
      790. nikkei2019_qual_a
      791. diverta2019_2_a Ball Distribution
      792. diverta2019_a Consecutive Integers
      793. code_festival_2017_quala_b fLIP
      794. abc254_f
      795. abc247_e Max Min
      796. abc247_f Cards
      797. abc246_a Four Points
      798. abc246_b Get Closer
      799. abc246_c Coupon
      800. abc246_d 2-variable Function
      801. abc246_e Bishop 2
      802. abc246_f typewriter
      803. abc245_d Polynomial division
      804. abc245_e Wrapping Chocolate
      805. abc244_a
      806. abc244_c Yamanote Line Game
      807. abc211_a
      808. abc211_b
      809. abc211_c
      810. abc211_d
      811. abc211_e
      812. arc137_c Distinct Numbers
      813. CF1672A Log Chopping
      814. CF1672B I love AAAB
      815. CF1672C Unequal Array
      816. CF1669D Colorful Stamp
      817. CF1660B Vlad and Candies
      818. CF1656A Good Pairs
      819. CF1656B Subtract Operation
      820. CF1656C Make Equal With Mod
      821. CF1654A
      822. CF1654B
      823. CF1654C
      824. CF1649A
      825. CF1649B
      826. CF1648A Weird Sum
      827. CF1648B
      828. CF1646C
      829. CF1634A
      830. CF1634B
      831. CF1634C
      832. CF1633A
      833. CF1633C
      834. CF1633D
      835. CF1632C
      836. CF1627A Not Shading
      837. CF1627B Not Sitting
      838. CF1627C Not Assigning
      839. CF1600E Array Game
      840. CF1260C Infinite Fence
      841. CF1294B Collecting Packages
      842. CF1251C Minimize The Integer
      843. CF1007A Reorder the Array
      844. CF721D Maxim and Array
      845. CF445B
      846. CF11D A Simple Task
      847. CF1669D Colorful Stamp
      848. CF1669E 2-Letter Strings
      849. CF1660B Vlad and Candies
      850. CF1656A Good Pairs
      851. CF1656B Subtract Operation
      852. CF1656C Make Equal With Mod
      853. CF1656D K-good
  96. spoj 题解
    1. HK
      1. ARRTWIST
  97. luogu 题解
  98. Leetcode 题解