├── 21-linearAlgebra ├── 矩阵练习.md └── images │ ├── 20180427154236975.png │ └── 20180427154248283.png ├── .gitignore ├── 02-simulate ├── images │ ├── sqr.png │ ├── carpet.png │ ├── pre_sum.png │ └── sqr_problem.png └── 小学数学题.md ├── 09-graph ├── images │ ├── p1038_1.png │ ├── p1038_2.png │ └── p1038_3.png └── 图论基础.md ├── 12-dp1 ├── images │ ├── uva10118.PNG │ └── uva10604.PNG ├── codes │ ├── luogu1049.cpp │ ├── luogu1164.cpp │ ├── luogu1048.cpp │ ├── luogu1616.cpp │ ├── luogu1060.cpp │ └── luogu1064.cpp ├── 区间调度问题.md ├── 背包2.md ├── 背包1.md └── 记忆化搜索.md ├── 16-binary ├── images │ ├── 1083.png │ ├── 1423469597.jpg │ └── 0_13121018578dRo.gif └── 二分与模拟.md ├── 19-numTheory ├── images │ └── TLE.png ├── 不定方程习题.md └── 质数筛专题.md ├── codes ├── luogu │ ├── luogu1010.cpp │ ├── luogu1019单词接龙.cpp │ ├── luogu1003铺地毯.cpp │ ├── luogu1226.cpp │ ├── luogu1967_truck.cpp │ └── luogu4927.cpp └── codevs │ └── codevs3287_truck.pas ├── 01-warmup └── images │ ├── treasure.png │ ├── dragon&tiger01.png │ └── dragon&tiger02.png ├── 06-searchModel ├── images │ └── whzNB.png └── 搜索建模1.md ├── 20-comMath ├── images │ ├── Tolerance1.png │ └── Tolerance2.png ├── 组合数常用结论.md ├── 抽屉原理及其习题.md ├── 组合数51nod专题.md └── 容斥.md ├── 13-DataStruct ├── images │ ├── poj1151_1.png │ ├── poj1151_2.png │ ├── poj1177_1.png │ ├── poj1177_2.png │ └── poj1177_3.png ├── 树状数组与逆序对.md └── 线段树2.md ├── 14-searchAgain └── images │ └── rotation_game.jpg ├── doc ├── tips.md ├── tg_knowledge.md ├── tg_outline.md └── log.md ├── 15-dp2 ├── luogu提高组dp.md └── 概率dp.md ├── 04-heap └── codes │ ├── luogu1801.cpp │ ├── luogu1090.cpp │ ├── luogu1631.cpp │ ├── luogu2278.cpp │ ├── luogu3419.cpp │ ├── luogu2085.cpp │ ├── luogu1484.cpp │ └── luogu2827.cpp ├── main.py ├── README.md ├── LICENSE ├── 11-graphAlgorithm2 ├── 二分图匹配(不含代码).md └── 二分图匈牙利.md ├── 18-string2 └── 回文树专题练习.md ├── 05-greedy └── 贪心算法练习.md ├── 08-string1 ├── kmp专题练习.md └── Manacher.md ├── 03-bigInteger └── 高精度算法.md ├── 10-graphAlgorithm1 └── 并查集.md └── 07-searchOptimization └── dfs优化.md /21-linearAlgebra/矩阵练习.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.ppt 2 | *.pptx 3 | *.doc 4 | *.docx 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /02-simulate/images/sqr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/02-simulate/images/sqr.png -------------------------------------------------------------------------------- /09-graph/images/p1038_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/09-graph/images/p1038_1.png -------------------------------------------------------------------------------- /09-graph/images/p1038_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/09-graph/images/p1038_2.png -------------------------------------------------------------------------------- /09-graph/images/p1038_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/09-graph/images/p1038_3.png -------------------------------------------------------------------------------- /12-dp1/images/uva10118.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/12-dp1/images/uva10118.PNG -------------------------------------------------------------------------------- /12-dp1/images/uva10604.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/12-dp1/images/uva10604.PNG -------------------------------------------------------------------------------- /16-binary/images/1083.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/16-binary/images/1083.png -------------------------------------------------------------------------------- /19-numTheory/images/TLE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/19-numTheory/images/TLE.png -------------------------------------------------------------------------------- /codes/luogu/luogu1010.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/codes/luogu/luogu1010.cpp -------------------------------------------------------------------------------- /01-warmup/images/treasure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/01-warmup/images/treasure.png -------------------------------------------------------------------------------- /02-simulate/images/carpet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/02-simulate/images/carpet.png -------------------------------------------------------------------------------- /codes/luogu/luogu1019单词接龙.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/codes/luogu/luogu1019单词接龙.cpp -------------------------------------------------------------------------------- /02-simulate/images/pre_sum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/02-simulate/images/pre_sum.png -------------------------------------------------------------------------------- /06-searchModel/images/whzNB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/06-searchModel/images/whzNB.png -------------------------------------------------------------------------------- /16-binary/images/1423469597.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/16-binary/images/1423469597.jpg -------------------------------------------------------------------------------- /20-comMath/images/Tolerance1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/20-comMath/images/Tolerance1.png -------------------------------------------------------------------------------- /20-comMath/images/Tolerance2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/20-comMath/images/Tolerance2.png -------------------------------------------------------------------------------- /02-simulate/images/sqr_problem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/02-simulate/images/sqr_problem.png -------------------------------------------------------------------------------- /13-DataStruct/images/poj1151_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/13-DataStruct/images/poj1151_1.png -------------------------------------------------------------------------------- /13-DataStruct/images/poj1151_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/13-DataStruct/images/poj1151_2.png -------------------------------------------------------------------------------- /13-DataStruct/images/poj1177_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/13-DataStruct/images/poj1177_1.png -------------------------------------------------------------------------------- /13-DataStruct/images/poj1177_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/13-DataStruct/images/poj1177_2.png -------------------------------------------------------------------------------- /13-DataStruct/images/poj1177_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/13-DataStruct/images/poj1177_3.png -------------------------------------------------------------------------------- /01-warmup/images/dragon&tiger01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/01-warmup/images/dragon&tiger01.png -------------------------------------------------------------------------------- /01-warmup/images/dragon&tiger02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/01-warmup/images/dragon&tiger02.png -------------------------------------------------------------------------------- /16-binary/images/0_13121018578dRo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/16-binary/images/0_13121018578dRo.gif -------------------------------------------------------------------------------- /14-searchAgain/images/rotation_game.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/14-searchAgain/images/rotation_game.jpg -------------------------------------------------------------------------------- /21-linearAlgebra/images/20180427154236975.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/21-linearAlgebra/images/20180427154236975.png -------------------------------------------------------------------------------- /21-linearAlgebra/images/20180427154248283.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cww97/noip_tg/HEAD/21-linearAlgebra/images/20180427154248283.png -------------------------------------------------------------------------------- /doc/tips.md: -------------------------------------------------------------------------------- 1 | # 如何在班主任跪下来求你多带学生时候say no 2 | 3 | 多接课不需要操作 4 | 5 | ### 老人言 6 | 7 | - 真的不要心软啊,他们连我吃饭的时间都想占据,如果不拒绝的话 8 | - 又白说不让,我听又白的 9 | 10 | ### 反面典型 11 | 12 | - 我被说的愿意了。。感觉犯了不要心软的禁忌 13 | 14 | ## Summary 15 | 16 | 年轻人,总是要心软个一两次然后才会硬起来 17 | -------------------------------------------------------------------------------- /15-dp2/luogu提高组dp.md: -------------------------------------------------------------------------------- 1 | # luogu提高组dp 2 | 3 | ## 动态规划TG.lv(1) 4 | 5 | - P1005 矩阵取数游戏 6 | - P1373 小a和uim之大逃离 7 | - P2279 [HNOI2003]消防局的设立 8 | - P1220 关路灯 9 | - P1156 垃圾陷阱 10 | 11 | ## 动态规划TG.lv(2) 12 | 13 | - P1273 有线电视网 14 | - P1169 [ZJOI2007]棋盘制作 15 | - P2577 [ZJOI2005]午餐 16 | - P1070 道路游戏 17 | - P2051 [AHOI2009]中国象棋 18 | -------------------------------------------------------------------------------- /12-dp1/codes/luogu1049.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | const int maxc = 20000 + 10; 5 | const int maxn = 30 + 10; 6 | 7 | int v[maxn]; 8 | int dp[maxc] = {0}; 9 | 10 | int main() 11 | { 12 | int V,n; 13 | scanf("%d%d",&V,&n); 14 | for(int i = 1; i <= n; i++) scanf("%d",&v[i]); 15 | for(int i = 1; i <= n; i++) 16 | for(int j = V; j >= v[i]; j--) 17 | dp[j] = max(dp[j],dp[j-v[i]] + v[i]); 18 | printf("%d\n",V-dp[V]); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /12-dp1/codes/luogu1164.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | const int maxm = 10000 + 10; 6 | const int maxn = 100 + 10; 7 | 8 | int a[maxn]; 9 | int dp[maxm] = {0}; 10 | 11 | int main() 12 | { 13 | int n,m; 14 | dp[0] = 1; 15 | scanf("%d%d",&n,&m); 16 | for(int i = 1; i <= n; i++) 17 | { 18 | scanf("%d",&a[i]); 19 | for(int j = m; j >= a[i]; j--) 20 | dp[j] += dp[j-a[i]]; 21 | } 22 | printf("%d\n",dp[m]); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /12-dp1/codes/luogu1048.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | const int maxn = 100 + 10; 5 | const int maxc = 1000 + 10; 6 | 7 | int dp[maxc] = {0}; 8 | int v[maxn],w[maxn]; 9 | int main() 10 | { 11 | int T,M; 12 | scanf("%d%d",&T,&M); 13 | for(int i = 1; i <= M; i++) 14 | scanf("%d%d",&v[i],&w[i]); 15 | for(int i = 1; i <= M; i++) 16 | for(int j = T; j >= v[i]; j--) 17 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 18 | printf("%d\n",dp[T]); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /12-dp1/codes/luogu1616.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | const int maxc = 100000 + 10; 5 | const int maxn = 10000 + 10; 6 | 7 | int dp[maxc] = {0}; 8 | int v[maxn],w[maxn]; 9 | int main() 10 | { 11 | int T,M; 12 | scanf("%d%d",&T,&M); 13 | for(int i = 1; i <= M; i++) 14 | scanf("%d%d",&v[i],&w[i]); 15 | for(int i = 1; i <= M; i++) 16 | for(int j = v[i]; j <= T; j++) 17 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 18 | printf("%d\n",dp[T]); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /12-dp1/codes/luogu1060.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | const int maxm = 30; 6 | const int maxn = 30000 + 10; 7 | 8 | int v[maxm],w[maxm]; 9 | int dp[maxn]; 10 | 11 | int main() 12 | { 13 | int n,m; 14 | scanf("%d%d",&n,&m); 15 | for(int i = 1; i <= m; i++) 16 | { 17 | scanf("%d%d",&v[i],&w[i]); 18 | w[i] *= v[i]; 19 | } 20 | for(int i = 1; i <= m; i++) 21 | for(int j = n; j-v[i] >= 0; j--) 22 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 23 | printf("%d\n",dp[n]); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /04-heap/codes/luogu1801.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | int a[200005]; 6 | int l = 1, r, m, n; 7 | priority_queue, greater > qmin; 8 | priority_queue qmax; 9 | int main() { 10 | cin >> m >> n; 11 | for(int i = 1; i <= m; i++) 12 | scanf("%d", &a[i]); 13 | for(int i = 1; i <= n; i++) { 14 | scanf("%d", &r); 15 | for(int j = l; j <= r; j++) { 16 | qmax.push(a[j]); 17 | if(qmax.size() > i) { 18 | qmin.push(qmax.top()); 19 | qmax.pop(); 20 | } 21 | } 22 | l = r + 1; 23 | printf("%d\n", qmax.top()); 24 | if(!qmin.empty()) { 25 | qmax.push(qmin.top()); 26 | qmin.pop(); 27 | } 28 | } 29 | return 0; 30 | } -------------------------------------------------------------------------------- /codes/luogu/luogu1003铺地毯.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int N = 1e4 + 7; 4 | 5 | struct Carpet{ 6 | int a, b, g, k; 7 | 8 | void read(){ 9 | scanf("%d%d%d%d", &a, &b, &g, &k); 10 | } 11 | 12 | int cover(int x, int y){ 13 | return a<=x && x<=a+g && b<=y && y<=b+k; 14 | } 15 | } carpets[N]; 16 | 17 | int main(){ 18 | int n, x, y; 19 | scanf("%d", &n); 20 | for (int i = 1; i <= n; i++){ 21 | carpets[i].read(); 22 | } 23 | scanf("%d%d", &x, &y); 24 | 25 | int ans = -1; 26 | for (int i = n; i >= 1; i--){ 27 | if (carpets[i].cover(x, y)){ 28 | ans = i; 29 | break; 30 | } 31 | } 32 | printf("%d\n", ans); 33 | return 0; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /04-heap/codes/luogu1090.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | using namespace std; 4 | struct node { 5 | int m; 6 | bool operator<(const node &a) const { //优先队列,重量小的在前 7 | return a.m < m; 8 | } 9 | }; 10 | int n, Sum; 11 | node Num, temp, temp1; 12 | priority_queue a; 13 | int main() { 14 | Sum = 0; 15 | scanf("%d", &n); 16 | for(int i = 0; i < n; i++) { 17 | scanf("%d", &Num); 18 | a.push(Num); 19 | } 20 | for(int i = n; i > 1; i--) { 21 | temp.m = 0; 22 | temp = a.top(), a.pop(); 23 | temp1 = a.top(), a.pop(); 24 | 25 | temp.m = temp.m + temp1.m; 26 | Sum = Sum + temp.m; 27 | a.push(temp); 28 | //cout<<"???\n"; 29 | } 30 | printf("%d", Sum); 31 | return 0; 32 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pypandoc 2 | import os 3 | import re 4 | import fnmatch 5 | 6 | # modify this on different Computer 7 | output_path = "C:\\Users\\cww97\\Desktop\\output\\" 8 | 9 | 10 | def convert_mds(cwd, dir_name): 11 | os.chdir(cwd + '\\' + dir_name) 12 | 13 | files = os.listdir() 14 | files = fnmatch.filter(files, '*.md') 15 | for file in files: 16 | file_name = file.replace('.md', '') 17 | output_name = output_path + dir_name[:2] + 'P-' + file_name 18 | os.system('pandoc -s %s -o %s.docx' %(file, output_name)) 19 | print(file, '-->', file_name, '.docx') 20 | 21 | def main(): 22 | cwd = os.getcwd() 23 | dirs = os.listdir() 24 | for dir in dirs: 25 | if (re.match(r"\d\d\-.*", dir)): 26 | convert_mds(cwd, dir) 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /04-heap/codes/luogu1631.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int N = 1e5 + 7; 4 | int n; 5 | int a[N], b[N]; 6 | priority_queue Q; 7 | void Print(int x) { 8 | int tmp = Q.top(); 9 | Q.pop(); 10 | if(x == n) printf("%d ", tmp); 11 | else { 12 | Print(x + 1); 13 | printf("%d ", tmp); 14 | } 15 | } 16 | int main() { 17 | int flag; 18 | scanf("%d", &n); 19 | for(int i = 0; i < n; i++) scanf("%d", a + i); 20 | for(int i = 0; i < n; i++) scanf("%d", b + i); 21 | for(int i = 0; i < n; i++) Q.push(a[0] + b[i]); 22 | for(int i = 1; i < n; i++) { 23 | for(int j = 0; j < n; j++) { 24 | if(a[i] + b[j] < Q.top()) { 25 | Q.pop(); 26 | Q.push(a[i] + b[j]); 27 | } else break; 28 | } 29 | } 30 | Print(1); 31 | return 0; 32 | } -------------------------------------------------------------------------------- /04-heap/codes/luogu2278.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | struct node { 6 | int id, rea, nee, lev; 7 | bool operator <(const node &rhs) const { 8 | return lev == rhs.lev ? rea > rhs.rea : lev < rhs.lev; 9 | } 10 | }; 11 | node pre; 12 | int a, b, c, d, tnow = 0; 13 | priority_queueq; 14 | int main() { 15 | while(scanf("%d %d %d %d", &a, &b, &c, &d) != EOF) { 16 | pre.id = a; pre.rea = b; 17 | pre.nee = c; pre.lev = d; 18 | while(!q.empty() && tnow + q.top().nee <= pre.rea) { 19 | node fr = q.top(); 20 | q.pop(); 21 | printf("%d %d\n", fr.id, fr.nee + tnow); 22 | tnow += fr.nee; 23 | } 24 | if(!q.empty()) { 25 | node fr = q.top(); 26 | q.pop(); 27 | fr.nee = fr.nee - pre.rea + tnow; 28 | q.push(fr); 29 | } 30 | q.push(pre); 31 | tnow = pre.rea; 32 | } 33 | while(!q.empty()) { 34 | node fr = q.top(); 35 | q.pop(); 36 | tnow += fr.nee; 37 | printf("%d %d\n", fr.id, tnow); 38 | } 39 | return 0; 40 | } -------------------------------------------------------------------------------- /04-heap/codes/luogu3419.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | const int MAXN = 100010, MAXM = 500010; 7 | queue q[MAXN]; 8 | int NEXT[MAXM]; 9 | int a[MAXM]; 10 | int n, k, m; 11 | struct cmp { 12 | inline bool operator()(const int &x, const int &y) { 13 | return NEXT[x] < NEXT[y]; 14 | } 15 | }; 16 | priority_queue, cmp> pq; 17 | int inq[MAXN], ans; 18 | int main() { 19 | scanf("%d%d%d", &n, &k, &m); 20 | for(int i = 1; i <= m; i++) { 21 | scanf("%d", &a[i]); 22 | q[a[i]].push(i); 23 | } 24 | for(int i = 1; i <= m; i++) { 25 | q[a[i]].pop(); 26 | if(q[a[i]].empty())NEXT[i] = m + 1; 27 | else NEXT[i] = q[a[i]].front(); 28 | } 29 | for(int i = 1; i <= m; i++) { 30 | if(!inq[a[i]]) { 31 | if(pq.size() == k) { 32 | inq[a[pq.top()]] = 0; 33 | pq.pop(); 34 | } 35 | pq.push(i); 36 | ++ans; 37 | inq[a[i]] = 1; 38 | } else { 39 | ++k; 40 | pq.push(i); 41 | } 42 | } 43 | printf("%d\n", ans); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /04-heap/codes/luogu2085.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | const int N = 1e4 + 7; 4 | int n, m, cnt, cnt1; 5 | int vis[N][N]; 6 | struct F { 7 | int a, b, c; 8 | } F[N]; 9 | struct node { 10 | int x, y, fn; 11 | bool operator < (const node &b)const { 12 | return b.y < y; 13 | } 14 | } P[N]; 15 | int Calu(int t1, int t2, int t3, int _x) { 16 | return (_x * _x * t1 + _x * t2 + t3); 17 | } 18 | priority_queue Q; 19 | int main() { 20 | node temp; 21 | scanf("%d %d", &n, &m); 22 | for(int t1, t2, t3, i = 0; i < n; i++) { 23 | scanf("%d %d %d", &t1, &t2, &t3); 24 | F[i].a = t1; 25 | F[i].b = t2; 26 | F[i].c = t3; 27 | } 28 | for(int i = 0; i < n; i++) { 29 | P[cnt].fn = i; 30 | P[cnt].x = 1; 31 | P[cnt].y = Calu(F[i].a, F[i].b, F[i].c, P[cnt].x); 32 | cnt++; 33 | } 34 | for(int i = 0 ; i < cnt; i++) { 35 | Q.push(P[i]); 36 | } 37 | while(cnt1 < m) { 38 | temp = Q.top(); 39 | printf("%d ", temp.y); 40 | cnt1++; 41 | Q.pop(); 42 | temp.x++; 43 | temp.y = Calu(F[temp.fn].a, F[temp.fn].b, F[temp.fn].c, temp.x); 44 | Q.push(temp); 45 | } 46 | return 0; 47 | } -------------------------------------------------------------------------------- /codes/luogu/luogu1226.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | typedef long long LL; 4 | 5 | // (a + b) % mod == ((a % mod) + (b % mod)) % mod, * 6 | LL power0(LL a, LL x, LL mod){ 7 | LL ans = 1; 8 | for (int i = 1; i <= x; i++){ 9 | ans *= a; 10 | ans %= mod; 11 | } 12 | return ans % mod; 13 | } 14 | 15 | LL power(LL a, LL x, const LL &mod){ 16 | //a ^ x = (a ^(x/2)) ^ 2 17 | if (x == 0) return 1 % mod; 18 | LL tmp = power(a, x >> 1, mod); 19 | LL ans = tmp * tmp % mod; 20 | if (x % 2 == 1) ans *= a; 21 | return ans % mod; 22 | } 23 | 24 | //快速乘法 25 | LL fast_multi(LL m, LL n, LL mod){ 26 | LL ans = 0;//注意初始化是0,不是1 27 | n = (n % mod + mod) % mod; 28 | for (;n; n >>= 1){ 29 | if (n & 1) ans = (ans + m) % mod; 30 | m = (m + m) % mod;//和快速幂一样,只不过这里是加 31 | } 32 | return ans % mod; 33 | } 34 | LL fast_pow(LL a, LL n, LL mod){//快速幂 35 | LL ans = 1; 36 | for (;n;n >>= 1){ 37 | if (n & 1) ans = fast_multi(ans, a, mod) % mod;//不能直接乘 38 | a = fast_multi(a, a, mod) % mod; 39 | } 40 | return ans; 41 | } 42 | 43 | int main(){ 44 | LL b, p, k; 45 | scanf("%lld%lld%lld", &b, &p, &k); 46 | printf("%lld^%lld mod %lld=%lld\n", b, p, k, power(b, p, k)); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /04-heap/codes/luogu1484.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define reg register 6 | using namespace std; 7 | const int Maxn = 5e5 + 5; 8 | typedef long long ll; 9 | struct dug { 10 | int g, val; 11 | bool operator <(const dug &rhs) const { 12 | return val < rhs.val; 13 | } 14 | }; 15 | ll ans = 0, val[Maxn]; 16 | int n, k, fin = 0; 17 | bool sbp[Maxn]; 18 | priority_queue q; 19 | int l[Maxn], r[Maxn]; 20 | int main() { 21 | memset(sbp, 0, sizeof(sbp)); 22 | scanf("%d %d", &n, &k); 23 | for(int i = 1; i <= n; ++i) { 24 | l[i] = i - 1; 25 | r[i] = i + 1; 26 | scanf("%lld", &val[i]); 27 | q.push((dug) { 28 | i, val[i] 29 | }); 30 | } 31 | r[0] = 1; l[n + 1] = n; 32 | while(k--) { 33 | while(sbp[q.top().g])q.pop(); 34 | dug fr = q.top(); 35 | if(q.top().val < 0) break; 36 | q.pop(); 37 | ans += fr.val; 38 | int id = fr.g; 39 | sbp[r[id]] = sbp[l[id]] = 1; 40 | val[id] = val[l[id]] + val[r[id]] - val[id]; 41 | q.push((dug) { 42 | fr.g, val[id] 43 | }); 44 | l[id] = l[l[id]]; r[l[id]] = id; 45 | r[id] = r[r[id]]; l[r[id]] = id; 46 | } 47 | printf("%lld", ans); 48 | return 0; 49 | } -------------------------------------------------------------------------------- /12-dp1/codes/luogu1064.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | const int maxc = 32000 + 10; 5 | const int maxn = 100 + 10; 6 | typedef pair P; 7 | 8 | int c,m,u,p,q,tot = 0; 9 | map mp; 10 | vector

vs[maxn]; 11 | int dp[maxc]; 12 | int main() 13 | { 14 | scanf("%d%d",&c,&m); 15 | for(int i = 1; i <= m; i++) 16 | { 17 | scanf("%d%d%d",&u,&p,&q); 18 | if(q == 0) 19 | { 20 | mp[i] = ++tot; 21 | vs[tot].push_back(P(u,u*p)); 22 | } 23 | else vs[mp[q]].push_back(P(u,u*p)); 24 | } 25 | for(int i = 1; i <= tot; i++) 26 | for(int j = c; j >= vs[i][0].first; j--) 27 | { 28 | dp[j] = max(dp[j],dp[j - vs[i][0].first] + vs[i][0].second); 29 | if(vs[i].size() >= 2 && j - vs[i][0].first - vs[i][1].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][1].first] + vs[i][0].second + vs[i][1].second); 30 | if(vs[i].size() >= 3 && j - vs[i][0].first - vs[i][2].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][2].first] + vs[i][0].second + vs[i][2].second); 31 | if(vs[i].size() >= 3 && j - vs[i][0].first - vs[i][1].first - vs[i][2].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][1].first - vs[i][2].first] + vs[i][0].second + vs[i][1].second + vs[i][2].second); 32 | } 33 | printf("%d\n",dp[c]); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlldreamNOIP 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 4 | 5 | Hello, the curel world 6 | 7 | ## Outline 8 | 9 | Chapter | Content 10 | --- | --- 11 | 01热身练习 | 了解stl与常用读入的坑(file) 12 | 02模拟类问题| 复杂模拟,位运算, “小学”数学 13 | 03高精度计算| 彪悍的人生不需要解释 14 | 04堆| 彪悍的人生不需要解释 15 | 05贪心算法进阶| 贪心的一些比较难的题(简单的在普及) 16 | 06搜索建模 | 回顾DFS和BFS, 搜索建模(习题) 17 | 07搜索优化 | 剪枝优化, 双向搜索· 18 | 08字符串初阶 | KMP(find),Manacher 19 | 09图论基础 | 图的概念与遍历, 拓扑排序, 欧拉回路 20 | 10图论常用算法1 | 并查集->生成树, 最短路 21 | 11图论常用算法2 | 二分图(hungary, km) 22 | 12动态规划1 | 记忆化搜索, DP经典模型(线性背包区间树状) 23 | 13区间数据结构 | 树状数组, 线段树 24 | 14搜索进阶 | 迭代加深策略, A*搜索 25 | 15动态规划2 | 单调队列优化,习题123 26 | 16二分答案进阶 | 复杂题, 与各种算法相结合 27 | 17图论建模 | 模型构建, 最短路生成树变形 28 | 18字符串进阶 | tire, AC自动机 29 | 19初等数论 | 欧拉筛,中国剩余定理,同余,逆元,扩欧 30 | 20组合数学 | 常用组合数处理(杨辉费马卢卡斯二项式), 抽屉容斥 31 | 21线性代数 | 矩阵与快速幂, 高斯消元 32 | 22图论高级算法 | LCA, Tarjan, 网络流 33 | 23考试技巧 | 构造数据, 分段得分, 骗分 34 | 35 | ## Next Plan 36 | 37 | others 38 | 39 | - 组合数学ppt要拆成多个小专题(只管拆就行了,细化留给后来人) 40 | 41 | - 稍难的dp123(luogu)(分三周) 42 | 43 | ## markdown tips: 44 | 45 | - 最后我统一转成 `.docx` 上传 `teambition` 46 | - 注意文件名尽量不要包含空格与特殊字符(包括md文件与图片文件) 47 | - 引用图片在章节目录下创建`images`子文件夹,然后上传 48 | - 关于数学公式,`latex` 是个好东西,可是 `gayhub` 不兹瓷,不过pandoc兹瓷,所以大家可以写着(最好顺带附一张公式的截图),这样`github`和最后的`doc`文件都能很优雅,~~懒的话就贴张图吧~~ 49 | -------------------------------------------------------------------------------- /15-dp2/概率dp.md: -------------------------------------------------------------------------------- 1 | # 概率dp 2 | 3 | ## HDU1203 I NEED A OFFER! 4 | 5 | 测试网站[HDU1203](http://acm.hdu.edu.cn/showproblem.php?pid=1203) 6 | 7 | ### 题目描述 8 | 9 | Speakless很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了。 10 | 11 | 要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的。Speakless没有多少钱,总共只攒了n万美元。 12 | 13 | 他将在m个学校中选择若干的(当然要在他的经济承受范围内)。 14 | 15 | 每个学校都有不同的申请费用a(万美元),并且Speakless估计了他得到这个学校offer的可能性b。 16 | 17 | 不同学校之间是否得到offer不会互相影响。“I NEED A OFFER”,他大叫一声。 18 | 19 | 帮帮这个可怜的人吧,帮助他计算一下,他可以收到至少一份offer的最大概率。 20 | 21 | (如果Speakless选择了多个学校,得到任意一个学校的offer都可以)。 22 | 23 | ### 题目分析 24 | 25 | 概率dp问题 ,由于概率很小,直接相乘会导致浮点数误差。所以 取对数,化乘法为加法。 26 | 27 | ### 代码示例 28 | 29 | ```c++ 30 | #include 31 | 32 | using namespace std; 33 | typedef pair P; 34 | typedef long long LL; 35 | const int maxn = 11000; 36 | const int INF = 0x3f3f3f3f; 37 | int n,v; 38 | int vol[maxn]; 39 | double val0[maxn],val[maxn],dp[maxn]; 40 | int main() 41 | { 42 | while(scanf("%d%d",&v,&n) && (v || n)) 43 | { 44 | for(int i = 0; i <= v; i++) dp[i] = 0; 初始化dp 数组 45 | for(int i = 0; i < n; i++) scanf("%d%lf",&vol[i],&val0[i]); 46 | for(int i = 0; i < n; i++) val[i] = log2(1-val0[i]); 取对数 47 | for(int i = 0; i < n; i++) 48 | for(int j = v; j-vol[i] >= 0; j--) 49 | dp[j] = min(dp[j],dp[j-vol[i]]+val[i]); 概率dp状态转移 50 | double ans = 1-pow(2,dp[v]); 51 | printf("%.1lf",ans*100); 52 | printf("%%\n"); 53 | } 54 | return 0; 55 | } 56 | ``` 57 | 58 | 59 | -------------------------------------------------------------------------------- /doc/tg_knowledge.md: -------------------------------------------------------------------------------- 1 | # NOIP提高组大纲 2 | 3 | ## 一.竞赛入门 4 | 5 | - 顺序、选择、循环(洛谷 P2615 神奇的幻方) 6 | - 数组(前缀和的应用)与结构体 7 | - 递归、分治(洛谷P1498 南蛮图腾 P1010 幂次方) 8 | - 位运算 补码 反码 9 | - 编译、内存管理、文件操作 10 | 11 | ## 二. 基础算法 12 | 13 | - 模拟类问题(P1003, P1067, P1540, P1056, P1328, P1563) 14 | - 字符串处理(KMP,tire) 15 | - 高精度计算(P1601, P2142, P1303, P1255, P1604) 16 | - 排序(归并处理: P1309 瑞士轮 P2827 蚯蚓 P4829 kry loves 2048) 17 | - 贪心(直观&最好需要数学证明最优性-调整法)(P1090, P1181, P1208, P1223, P1094, P1803, P1031, P1080, P1080, P1158) 18 | 19 | ## 三. 数据结构 20 | 21 | - 栈(单调栈) && 队列(单调队列) 22 | - 树和二叉树 23 | - 优先队列和堆 24 | - 树状数组 25 | - 线段树 26 | 27 | ## 四. 算法精讲 28 | 29 | ### 1. 图论 30 | 31 | - 图的概念与遍历 32 | - 图的最短路径,最短路变形(dj堆优化, spfa) 33 | - 并查集(tree), 生成树 34 | - 拓扑排序, 欧拉回路 35 | - 二分图 36 | - 强联通(*) 37 | 38 | ### 2. 搜索 39 | - DFS和BFS 40 | - 隐式图转化(搜索建模) 41 | - 剪枝优化(1.可行性优化 2.最优性优化 3.记忆化)(P1092 虫食算 P1731, 生日蛋糕 P3230比赛) 42 | - 迭代加深策略 43 | - 搜索顺序的选择 44 | - 双向搜索(*) 45 | - 启发式搜索A*(*) 46 | 47 | ### 2.动态规划(dynamic programming, dp) 48 | - DP初步(-) 49 | - 记忆化搜索 50 | - DP经典模型(线性、背包、区间、树状) 51 | - DP的优化策略(单调队列优化背包问题51nod-1158 全是1的最大子矩阵) 52 | (一切形式如f[i]=min/max(g[j]) 且需要满足条件a[i]>=a[j]或者a[i]<=a[j]形式的动态规划,都可以采用单调队列优化)(jcx) 53 | - 状态压缩 54 | - 数位DP 55 | 56 | ### 3. 二分 57 | 58 | 59 | 60 | 61 | ### 5.数论 62 | 63 | - 初等数论:欧拉筛,中国剩余定理,同余,逆元,扩欧 64 | - 组合数学:常用组合数处理(杨辉,费马小定理,卢卡斯定理),容斥原理,二项式 65 | - 博弈论:nim游戏,sg函数 * 66 | - 概率期望:随机算法(Monte-Carlo 暴力算概率) * 67 | - 线性代数:矩阵快速幂,N元一次方程,高斯消元,(NTT, FFT)(jcx) * 68 | 69 | ## 五、技巧&STL 70 | 71 | - 常用STL 72 | - 对拍, 构造数据 73 | - 读入优化 74 | - 分段得分 75 | - 骗分 76 | -------------------------------------------------------------------------------- /04-heap/codes/luogu2827.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | const int MAXN = 1e5 + 10; 9 | const int MAXM = 7 * 1e6 + 10; 10 | int n, m, q, v, t, a[MAXN], den, size, ans[MAXN + MAXM], u; 11 | double p; 12 | bool cmp(int a, int b) { 13 | return a > b; 14 | } 15 | queue e[3]; 16 | int getMax() { 17 | int res = -1; 18 | for(int i = 0; i < 3; i++) { 19 | if((!e[i].empty()) && (res == -1 || e[i].front() > e[res].front())) 20 | res = i; 21 | } 22 | return res; 23 | } 24 | int main() { 25 | scanf("%d %d %d %d %d %d", &n, &m, &q, &u, &v, &t); 26 | for(int i = 1; i <= n; i++) 27 | scanf("%d", &a[i]); 28 | sort(a + 1, a + n + 1, cmp); 29 | for(int i = 1; i <= n; i++) 30 | e[0].push(a[i]); 31 | for(int i = 1; i <= m; i++) { 32 | int f1 = 0, f2 = 0, f3 = 0; 33 | int id = getMax(); 34 | int sope = (i - 1) * q; 35 | int fx = e[id].front(); 36 | e[id].pop(); 37 | fx += sope; 38 | if(!(i % t)) 39 | printf("%d ", fx); 40 | int fir = (long long)fx * u / v;; 41 | int sec = fx - fir; 42 | fir -= (sope + q); 43 | sec -= (sope + q); 44 | e[1].push(fir); 45 | e[2].push(sec); 46 | } 47 | printf("\n"); 48 | int sope = q * m; 49 | for(int i = 1; i <= n + m; i++) { 50 | int id = getMax(); 51 | int fx = e[id].front(); 52 | e[id].pop(); 53 | if(!(i % t)) 54 | printf("%d ", fx + sope); 55 | } 56 | return 0; 57 | } -------------------------------------------------------------------------------- /doc/tg_outline.md: -------------------------------------------------------------------------------- 1 | # 提高组大纲 2 | 3 | ## 一.普及复习 4 | 5 | - **热身练习**: 简单模拟, dfs, bfs, 简单dp, 位运算简单技巧(sum_xor) 6 | - **模拟类问题**:P1003 铺地毯,P1067 多项式输出, P1540 机器翻译, P1056 排座椅, P1328 生活大爆炸版石头剪刀布, P1563 玩具谜题, P1023 税收与补贴问题, P1031 均分纸牌, P1042 乒乓球, P1086 花生采摘, P1098 字符串的展开 7 | - **高精度计算**(P1601, P2142, P1303, P1255, P1604) 8 | 9 | 10 | ## 二.常规知识点 11 | 12 | - **堆和堆排序**(优先队列) 13 | - **贪心算法进阶**: (直观&最好需要数学证明最优性-调整法)(P1090, P1181, P1208, P1223, P1094, P1803, P1031, P1080, P1080, P1158)(主要练习sort, struct, cmp, 优先队列) 14 | - **搜索建模**: DFS和BFS, 隐式图转化(搜索建模), 15 | - **搜索优化**: 剪枝优化(1.可行性优化 2.最优性优化 3.记忆化)(P1092 虫食算 P1731, 生日蛋糕 P3230比赛) 16 | - **字符串初阶**: (KMP(find)) 17 | - **图论基础**: 图的概念与遍历, 拓扑排序, 欧拉回路 18 | - **图论常用算法1**: 并查集(tree), 生成树(kruskal, prim), 19 | - **图论常用算法2**: 图的最短路径,最短路变形(dj堆优化, spfa(Bellman Ford), floyd) 20 | - **动态规划1**: 记忆化搜索, DP经典模型(线性、背包、区间、树状) 21 | - **区间数据结构**: 树状数组, 线段树 22 | - **搜索进阶**: 迭代加深策略, 搜索顺序的选择 23 | - **动态规划2**: 优化(单调队列优化背包问题51nod-1158 全是1的最大子矩阵)(一切形式如f[i]=min/max(g[j]) 且需要满足条件a[i]>=a[j]或者a[i]<=a[j]形式的动态规划,都可以采用单调队列优化)(jcx) 24 | - **二分答案进阶**: (初阶在普及)借教室, 架设电话线, 关押罪犯, 聪明的质检员 25 | - **图论问题建模**: 一般问题对图论问题的转换,spfa引擎的dp 26 | - **字符串进阶**: (tire) 27 | - **初等数论**: 欧拉筛,中国剩余定理,同余,逆元,扩欧 28 | - **组合数学**: 常用组合数处理(杨辉,费马小定理,卢卡斯定理),容斥原理,二项式 29 | - **树型结构**: 倍增 && LCA问题 30 | - **考试技巧**: 构造数据, 分段得分, 骗分 31 | 32 | ## 省选(各凭本事) 33 | 34 | - 对拍 35 | - 读入优化 36 | - 可持久化线段树(主席树) 37 | - 位运算 补码 反码 38 | - 双向搜索, 启发式搜索A*(*) 39 | - 动态规划3: 状态压缩,数位DP 40 | - maxflow 41 | - 二分图(hungary, maxflow) 42 | - 强联通(tarjan) 43 | - 博弈论:nim游戏,sg函数 * 44 | - 概率期望:随机算法(Monte-Carlo 暴力算概率) * 45 | - 线性代数:矩阵快速幂,N元一次方程,高斯消元,(NTT, FFT)(jcx) * 46 | - 滑动窗口(洛谷1090,洛谷1638,leetcode上的2sum、3sum) 47 | - 差分约束系统 48 | - Matrix-Tree定理 49 | - 群论,burnside引理与Polya定理 50 | - Baby step giant step 51 | - 欧拉函数线性筛(积性函数,狄利克雷卷积) 52 | - Dancing Links DLX 53 | - 各种平衡树,Link-Cut Tree 54 | - 分块,块状链表 55 | -------------------------------------------------------------------------------- /02-simulate/小学数学题.md: -------------------------------------------------------------------------------- 1 | # 小学数学题 2 | 3 | 本章节考点小学数学难度,不过看出来需要一些,你懂我意思吧 4 | 5 | ## 进制转换 6 | 7 | - 进制转换,前缀和 https://acm.ecnu.edu.cn/problem/3679/ 8 | 9 | 他现在想知道在十进制范围 [l,r] 内有多少整数满足在 k 进制下末尾恰好有 m 个 0。 10 | 11 | 比如在十进制下的 24 在二进制下是 11000,我们称十进制下的 24 在二进制下末尾恰好有 3 个 0。 12 | 13 | QQ 小方一筹莫展,你能帮他解决问题吗? 14 | 15 | ### 思路 16 | 17 | ![](images/pre_sum.png) 18 | 19 | ### Code 20 | 21 | ```c++ 22 | #include 23 | using namespace std; 24 | typedef long long LL; 25 | 26 | LL l, r, k, m; 27 | 28 | LL fpow(LL a, LL b, LL up) { 29 | if(b * log(a) > log(up)) return -1; 30 | LL ret = 1; 31 | for(int i = 1; i <= b; i++) { 32 | ret = ret * a; 33 | if(ret > up) return -1; 34 | } 35 | return ret; 36 | } 37 | 38 | LL solve(LL l, LL r, LL k, LL m) { 39 | // [l, r] x * k ^ m 40 | LL base = fpow(k, m, r); 41 | if (base == -1) return 0; 42 | LL R = r / base; 43 | LL L = (l - 1) / base; 44 | return R - L; 45 | } 46 | 47 | int main () { 48 | int T; for (scanf("%d", &T); T--; ) { 49 | scanf("%lld%lld%lld%lld", &l, &r, &k, &m); 50 | LL res = solve(l, r, k, m) - solve(l, r, k, m + 1); 51 | printf("%lld\n", res); 52 | } 53 | return 0; 54 | } 55 | ``` 56 | 57 | 58 | ## 方差 59 | 60 | - 完全平法,方差 https://acm.ecnu.edu.cn/problem/3678/ 61 | 62 | ![](images/sqr_problem.png) 63 | 64 | ### 题解 65 | 66 | ![](images/sqr.png) 67 | 68 | ### 代码 69 | 70 | 71 | ```c++ 72 | #include 73 | #define sqr(x) x*x 74 | using namespace std; 75 | typedef long long LL; 76 | const int N = 1e6 + 7; 77 | 78 | LL n, m, x[N]; 79 | 80 | int main(){ 81 | scanf("%lld%lld", &n, &m); 82 | for (int i = 1; i <= n; i++){ 83 | scanf("%lld", &x[i]); 84 | } 85 | sort(x + 1, x + n+1); 86 | 87 | LL sum = 0, sum2 = 0; 88 | for (int i = 1; i <= m; i++){ 89 | sum += x[i]; 90 | sum2 += sqr(x[i]); 91 | } 92 | 93 | LL ans = m * sum2 - sqr(sum); 94 | for (int i = m+1; i <= n; i++){ 95 | sum += x[i] - x[i-m]; 96 | sum2 += sqr(x[i]) - sqr(x[i-m]); 97 | ans = min(ans, m*sum2 - sqr(sum)); 98 | } 99 | printf("%lld\n", ans); 100 | return 0; 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /doc/log.md: -------------------------------------------------------------------------------- 1 | # Draft 2 | 3 | ## Who 4 | 5 | (排序为字典序) 6 | 7 | id |who 8 | --- | --- 9 | Abeillezhou| 周博 10 | beckywcn | 王晨妮 11 | cww97 | 陈伟文 12 | Dust-heart | 姚金辉 13 | F-TD5X | 蒋涵欣 14 | lianfengs | 蒋程旭 15 | littlefall | 齐智 16 | lwgkzl | 刘文阁 17 | phython96 | 蔡少斐 18 | refrainnb | 李泽鹏 19 | Tawn0000 | 凌晓 20 | zoesky1 | 朱明清 21 | 22 | ## 2019.3.20 23 | 24 | - 大四要忙毕设的:lwg, zmq, jhx, csf 25 | - 大三的能打的: wcn, lzp, yjh, jjm 26 | 27 | 毕业论文放大假了 28 | 29 | ## 2019.3.13 30 | 31 | - lwg: ~~高斯消元~~ 32 | - wcn: ~~单调栈~~ 33 | - jhx: ~~KM算法~~ 34 | - csf: ~~论文了论文了~~ 35 | - yjh: ~~数位dp~~ 36 | - lzp: ~~下周见~~ 37 | - jjm: tarjan题目 38 | 39 | ## 2019.3.6 40 | 41 | - lwg: ~~lca题目,emmmm这次需要稍微晚一点交货了今晚或明天~~ 42 | - wcn: ~~质数晒专题题目充实~~ 43 | - jhx: 马拉车习题 44 | - csf: 区间dp与概率dp加题目 45 | - yjh: ~~二分与图论模拟~~ 46 | - lzp: ~~同余专题题目,感冒下周~~ 47 | - zmq: ~~做毕设,五月继续~~ 48 | - jjm: ~~3.9 3.10号还有两场~~ 49 | 50 | ## 2019.2.27 51 | 52 | - zmq: ~~基础哈希,做毕设,五月继续~~ 53 | - lwg: ~~ac自动机~~ 54 | - wcn: ~~并查集~~ 55 | - jhx: ~~容斥~~ 56 | - csf: ~~抽屉原理~~ 57 | - lx: ~~stl 与 读入~~ 58 | - yjh: ~~最短路生成树完善~~ 59 | - jjm: ~~补考,休息两周,继续休假~~ 60 | 61 | ## 2019.2.20 62 | 63 | - zmq: ~~回文树课件~~ 64 | - lwg: ~~课件:马拉车~~ 65 | - wcn: ~~dfs优化~~ 66 | - jhx: ~~双向bfs~~ 67 | - csf: ~~数论,“刚几天没时间,一有时间我就补上”, 扩欧习题~~ 68 | - lx: 启发式搜索,迭代加深搜索, “正在做” 69 | - yjh: ~~贪心~~ 70 | - jjm: ~~补考,休息两周~~ 71 | 72 | ## 2019.2.14 73 | 74 | - ~~zmq: 回文树~~ 75 | - ~~wcn: kmp, next lcs~~ 76 | - csf: 数论,“刚几天没时间,一有时间我就补上” 77 | - lx: ~~背包专题,“我今晚交,明早检查吧”~~ 78 | - yjh: ~~图论建模,“我晚上前会贴上去的”~~ 79 | - jjm: ~~优先队列~~, 牛逼 80 | - lwg: ~~tire~~ 81 | - jxh: ~~图论基本概念课件~~ 82 | 83 | ## 2019.1.24 84 | 85 | - zmq:最短路 86 | - lx:二分图 87 | - jjm: last week 88 | - yjh: 字符串进阶,trie 89 | 90 | ## 2019.1.17 91 | 92 | - zmq: 最短路生成树题目 93 | - lx: 网络流题目 94 | - **jjm: 哈希** 95 | 96 | ## 2019.1.10 97 | 98 | - lx: 网络流入门 99 | 100 | ## 2019.1.3 101 | 102 | - lx: 位运算 103 | - zmq: 哈希 104 | - hsq: 状压 105 | 106 | ## 完成历史使命 107 | 108 | Schedule 109 | 110 | 章节|课件进度 | 题目进度 | 题目总结 111 | --- | --- |---| --- 112 | 01 热身 | 路天一 | 朱景亮 | 陈伟文 113 | 02 模拟 | 路天一 | 周博 | 陈伟文 114 | 03 高精度 | 路天一 | - | 陈伟文 115 | 04 堆 | 路天一 | - | 吉嘉铭牛逼 116 | 05 贪心进阶 | 蒋承旭(3/3) | - | 姚金辉 117 | 06 搜索建模 | 陈伟文 | 朱明清 | 陈伟文 118 | 07 搜索优化 | 陈伟文 | - | 王晨昵, 蒋涵欣 119 | 08 字符串初阶 | 路天一 | - | 王晨妮,刘文阁 120 | 09 图论基础| 王浩泽,蒋涵欣 | 朱明清 | 陈伟文 121 | 10 图论算法1 | 路天一 | 朱明清 | 陈伟文 122 | 11 图论算法2 | 陈伟文 | 凌晓 | 陈伟文 123 | 12 动态规划1 | 吉嘉铭(5/5) | 朱明清, 凌晓 | 陈伟文 124 | 13 区间数据结构| 路天一 | 朱明清 | 陈伟文 125 | 14 搜索进阶 | 陈伟文 | - | 蒋涵欣 126 | 15 动态规划2 | 蒋承旭 | - | jjm 127 | 16 二分答案进阶 | 吉嘉铭, 陈伟文 | - | 陈伟文 128 | 17 图论问题建模 | 陈伟文 | - | 姚金辉 129 | 18 字符串进阶 | 路天一 | 姚金辉 | 朱明清 130 | 19 初等数论 | 蒋承旭(2/2) | - | 蔡绍斐 131 | 20 组合数学 | 王浩泽 | 姚金辉 | 陈伟文 132 | 21 线性代数 | 姚金辉 | 姚金辉 | 陈伟文 133 | 22 图论高级算法 | 郭恒康 | 凌晓 | 陈伟文 134 | 23 考试技巧 | 陈伟文 | 陈伟文 | 陈伟文 135 | 136 | ## 省选(可能这辈子都做不完了) 137 | 138 | - 博弈论??? -------------------------------------------------------------------------------- /20-comMath/组合数常用结论.md: -------------------------------------------------------------------------------- 1 | ## 组合数学 2 | 3 | ### 常用公式和找规律 4 | 5 | ```C++ 6 | 7 | 斯特林公式 8 | n! 约等于 sqrt(2*pi*n)*pow(1.0*n/e,n) 9 | 10 | 带标号连通图计数 11 | 1 1 1 4 38 728 26704 1866256 251548592 12 | h(n)=2^(n(n-1)/2) 13 | f(n) = h(n)-sum{C(n-1,k-1)*f(k)*h(n-k)}(k=1...n-1) 14 | 15 | 不带标号n个节点的有根树计数 16 | 1, 1, 2, 4, 9, 20, 48, 115, 286, 719, 1842, 17 | 18 | 不带标号n个节点的树的计数 19 | 1,2,3,6,11,23,47,106,235 20 | OEIS 21 | A(x) = 1 + T(x) - T^2(x)/2 + T(x^2)/2, where T(x) = x + x^2 + 2*x^3 + ... is the g.f. for A000081 22 | 23 | 错排公式 24 | D[1] = 0; D[2] = 1; 25 | for(int i = 3; i < 25; i++) { 26 | D[i] = (i - 1) * (D[i - 1] + D[i - 2]); 27 | } 28 | 29 | 卡特兰数 30 | 1 2 5 14 42 132 429 1430 4862 16796 31 | binomial(2*n, n)-binomial(2*n, n-1) 32 | Sum_{k=0..n-1} a(k)a(n-1-k) 33 | 34 | 35 | Stirling数,又称为斯特灵数。 36 |   在组合数学,Stirling数可指两类数,都是由18世纪数学家James Stirling提出的。 37 |   第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。 38 |   第二类Stirling数是把包含n个元素的集合划分为正好k个非空子集的方法的数目。 39 | 递推公式 40 |   第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。 41 |   递推公式为, 42 |   S(n,0) = 0, S(1,1) = 1. 43 |   S(n 1,k) = S(n,k-1) nS(n,k)。 44 |   第二类Stirling数是把包含n个元素的集合划分为正好k个非空子集的方法的数目。 45 |   递推公式为: 46 |   S(n,k)=0; (n n) return 0; 89 | LL ans = 1; 90 | for(int i = 1; i <= m; i++){ 91 | LL a = (n + i - m) % MOD; 92 | LL b = i % MOD; 93 | ans = ans * (a * quick_mod(b, p-2) % MOD) % MOD; 94 | } 95 | return ans; 96 | } 97 | ``` 98 | 99 | 如果n,m很大 达到1e18,但是p很小 <= 1e5 ,我们可以利用这个p 100 | 101 | Lucas定理:C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p 102 | 103 | ```c++ 104 | LL Lucas(LL n, LL m){ 105 | if(m == 0) return 1; 106 | return C(n % p, m % p) * Lucas(n / p, m / p) % p; 107 | } 108 | void InitFac(){//阶乘预处理 109 | fac[0] = 1; 110 | for(int i=1; i<=n; i++) 111 | fac[i] = (fac[i-1] * i) % MOD; 112 | } 113 | LL C(LL n,LL m,LL p,LL fac[]){ 114 | if(n < m) return 0; 115 | return fac[n] * quick_mod(fac[m] * fac[n-m], p - 2, p) % p; 116 | } 117 | ``` 118 | 119 | 组合数奇偶性结论: 120 | 121 | 如果(n&m) == m 那么c(m,n)是奇数,否则是偶数 -------------------------------------------------------------------------------- /20-comMath/抽屉原理及其习题.md: -------------------------------------------------------------------------------- 1 | # 抽屉原理 2 | 3 | ## 概述 4 | 桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面至少放两个苹果。这一现象就是我们所说的“抽屉原理”。 5 | 抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1个元素放到n个集合中去,其中必定有一个集合里至少有两个元素。” 6 | 抽屉原理有时也被称为鸽巢原理。它是组合数学中一个重要的原理。 7 | 8 | --- 9 | 10 | ## 习题一、Halloween treats Hdu-1808 11 | 12 | 题目连接:https://vjudge.net/contest/284719#problem/A 13 | 14 | ### 题目描述 15 | 16 | 这道题的大致意思是,给出$n$个邻居,第$i$个邻居有$a_i$块糖果,你现在需要选定一些邻居去拜访,当你拜访一个邻居时,这个邻居会给予你$a_i$块糖果. 17 | 你需要令你手里的糖果数是$c$的倍数.输出你拜访邻居的方案. 18 | 值得注意的是:$c \le n$,这是一个非常重要的条件. 19 | 20 | ### 题解 21 | 22 | 如果我们暴力去枚举判定的话,时间复杂度非常高,应该是$n2^n$级别的,显然无法通过这道题.因此我们只能通过挖掘这道题目的性质来做啦. 23 | 24 | 如果我们把邻居们铺成一列,并且对它们求一个前缀和数组.我们继续思考,是否可以通过选取其中的一段区间的和来达到目标呢? 25 | 26 | 假如我们选取的区间$[i+1,j]$满足这样的条件,那么意味着$(sum(j) - sum(i)) \% c == 0$,也就是说$sum(j) \equiv sum(i) 在(\%c)的意义下$. 27 | 28 | 那么问题看似出现了转机,我们可以让所有的前缀和$sum(i)$对$c$取模,那么得到的结果必然属于$[0,c-1]$之间,如果存在一个前缀和在模$c$的意义下等于$0$ 29 | 30 | 那么就可以直接输出答案了,否则的话取模结果就只剩$c-1$种情况了,我们看成是$c-1$个盒子,而我们的前缀和个数为$n \ge c \gt c-1$,根据抽屉原理 31 | 32 | 必然有至少两个前缀和同余,于是最后需要选定的区间就介于这两个前缀和中间了. 33 | 34 | ### 代码 35 | 36 | 略 37 | 38 | --- 39 | 40 | ## 习题二、吃糖果 Hdu-1205 41 | 42 | 题目链接:https://vjudge.net/contest/284719#problem/B 43 | 44 | ### 题目描述 45 | 46 | 一个人手里有好几堆糖果,每一堆中的糖果种类是一样的,不同堆得糖果数目是不相同的. 47 | 48 | 这个人吃糖果时有个特殊的癖好,就是不喜欢将一样的糖果放在一起吃,喜欢先吃一种,下一次吃另一种. 49 | 50 | 请你帮忙判断一下,他是否有办法将所有的糖果都吃完. 51 | 52 | ### 题解 53 | 54 | 我们这样思考,我们将吃糖果的过程按照时间先后顺序排成一个序列的时候,那么必然相邻的两颗糖果他们的种类是不一样的. 55 | 56 | 而其中最重要的那种糖果就是数量最多的这种糖果,我们只需要令这类糖果在序列中不相邻,那么剩下的糖果都很好办,可以依次往这类糖果中间添加即可,相当于使用隔板. 57 | 58 | 举个栗子: 59 | 60 | 5a 2b 2c 61 | 62 | 先了列出a糖果的序列: 63 | 64 | a a a a a 65 | 66 | 使得a糖果不相邻,就要用剩下的糖果去填补,可知,剩下的糖果数目一定要大于等于MAX-1. 67 | 68 | a b a b a c a c 69 | 70 | 这样就可以了. 71 | 72 | 再举个栗子: 73 | 74 | 5a 4b 3c 75 | 76 | 这样怎么办呢? 77 | 78 | 照旧,先把a糖果的序列列出来: 79 | 80 | a a a a a 81 | 82 | 使得a糖果不相邻,就要用剩下的糖果去填补: 83 | 84 | a b a b a b a b 85 | 86 | 但是c糖果还没用怎么办呢?,可以紧紧跟随b糖果放置呀,相当于加厚隔板. 87 | 88 | a bc a bc a bc a b a 89 | 90 | 再再举个栗子: 91 | 92 | 5a 2b 2c 3d 1e 93 | 94 | a a a a a 95 | 96 | a b a b a c a c 97 | 98 | a bd a bd a cd a ce a 99 | 100 | ... 101 | 102 | 看明白了吧..... 103 | 104 | 所以,如果糖果总数$sum$,减去种类最多的糖果数$MAX$,得到的结果不小于$MAX-1$(也就是隔板的数量),那必然可行. 105 | 106 | 107 | ### 代码 108 | 109 | 略 110 | 111 | --- 112 | 113 | ## Harmony Forever HDU-3303 114 | 题目链接:https://vjudge.net/contest/284719#problem/C 115 | 116 | ### 题目描述 117 | 118 | 就是对一个集合,有两种操作: 119 | 120 | 1、将某个元素加入集合中; 121 | 122 | 2、一个是问当前集合中mod y 最小的数,如果有多个,输出最后加入的那个元素,要求的是输出他们加入集合的时间。 123 | 124 | ### 题解 125 | 126 | 根据抽屉原理,$[0,M-1]$之间的所有数在\$%M$的意义下都是不同的. 127 | 128 | 那么可以利用这个性质将要查找所有区间分成$[0,Y-1],[Y,2*Y-1],[2*Y,3*Y-1]....$,直到区间覆盖了所有的数. 129 | 130 | 然后我们只需要找各个区间的最小值就够了,这个可以用线段树来解决.找到每个区间的最小值之后,再将各个区间的最小值取模进行比较. 131 | 132 | 注意,这里虽然利用线段树可以解决问题,但如果说要查找的Y很小,那么分得的区间会很多,这样查找效率就会低,不如直接查找,所以这里又有一个小技巧,当Y比较小时,可以直接线性查找 133 | 134 | ### 代码 135 | 136 | 略 137 | 138 | --- 139 | 140 | ## Gopher II POJ-2536 141 | 142 | ### 题目描述 143 | 144 | 在一个二维平面上有$n$个高尔夫球和$m$个高尔夫球洞.每个高尔夫球洞只能容纳一个高尔夫球,每个高尔夫球的运动速度为$v$,每只球都想运动到洞里. 145 | 146 | 现在请问你,在$s$秒内,要使得场上的高尔夫球数量最少,你有什么样的策略,求出$s$秒后场上最少的球数量. 147 | 148 | ### 题解 149 | 150 | 这道题我感觉和抽屉原理的关系并不大呀. 151 | 152 | 我们可以对于每个球求出它$s$秒内可以到达那些洞,构建一个二分图,如果球和洞可达,那么就连一条边,最后跑一个二分图匹配就可以啦. 153 | 154 | 再用球的总数减去最大匹配的值,就是我们要求的答案. 155 | 156 | ### 代码 157 | 158 | 略 159 | 160 | --- 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2 | 3 | Anti 996 License Version 1.0 (Draft) 4 | 5 | Permission is hereby granted to any individual or legal entity 6 | obtaining a copy of this licensed work (including the source code, 7 | documentation and/or related items, hereinafter collectively referred 8 | to as the "licensed work"), free of charge, to deal with the licensed 9 | work for any purpose, including without limitation, the rights to use, 10 | reproduce, modify, prepare derivative works of, distribute, publish 11 | and sublicense the licensed work, subject to the following conditions: 12 | 13 | 1. The individual or the legal entity must conspicuously display, 14 | without modification, this License and the notice on each redistributed 15 | or derivative copy of the Licensed Work. 16 | 17 | 2. The individual or the legal entity must strictly comply with all 18 | applicable laws, regulations, rules and standards of the jurisdiction 19 | relating to labor and employment where the individual is physically 20 | located or where the individual was born or naturalized; or where the 21 | legal entity is registered or is operating (whichever is stricter). In 22 | case that the jurisdiction has no such laws, regulations, rules and 23 | standards or its laws, regulations, rules and standards are 24 | unenforceable, the individual or the legal entity are required to 25 | comply with Core International Labor Standards. 26 | 27 | 3. The individual or the legal entity shall not induce or force its 28 | employee(s), whether full-time or part-time, or its independent 29 | contractor(s), in any methods, to agree in oral or written form, to 30 | directly or indirectly restrict, weaken or relinquish his or her 31 | rights or remedies under such laws, regulations, rules and standards 32 | relating to labor and employment as mentioned above, no matter whether 33 | such written or oral agreement are enforceable under the laws of the 34 | said jurisdiction, nor shall such individual or the legal entity 35 | limit, in any methods, the rights of its employee(s) or independent 36 | contractor(s) from reporting or complaining to the copyright holder or 37 | relevant authorities monitoring the compliance of the license about 38 | its violation(s) of the said license. 39 | 40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 47 | 48 | 版权所有(c)<年份><版权持有人> 49 | 50 | 反996许可证版本1.0 51 | 52 | 在符合下列条件的情况下,特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以下 53 | 统称为“授权作品”)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括但 54 | 不限于使用、复制,修改,衍生利用、散布,发布和再许可: 55 | 56 | 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不得自行修 57 | 改。 58 | 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或经营地 59 | (以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和标准。如果该司法管辖 60 | 区没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可执行,则个人或法人实体必须遵守国 61 | 际劳工标准的核心公约。 62 | 3. 个人或法人不得以任何方式诱导或强迫其全职或兼职员工或其独立承包人以口头或书面形式同意直接或 63 | 间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和标准保护的权利或补救 64 | 措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该等个人或法人实体也不得以任何方法 65 | 限制其雇员或独立承包人向版权持有人或监督许可证合规情况的有关当局报告或投诉上述违反许可证的行为 66 | 的权利。 67 | 68 | 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵 69 | 权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均不承担因本软件或 70 | 本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。 -------------------------------------------------------------------------------- /codes/codevs/codevs3287_truck.pas: -------------------------------------------------------------------------------- 1 | { 2 | 作者:CWW970329 3 | 题目:p3287 货车运输 4 | } 5 | program cx; 6 | uses math; 7 | var i,m,n,sa,tg,treenum,cnt,qq,t:longint; 8 | home,ax,ay,aw,gym,head,next,e,w,h:array[0..100000]of longint; 9 | q:array[0..1000000]of longint; 10 | v:array[0..100000]of boolean; 11 | f,g:array[0..10000,0..20]of longint; 12 | 13 | function find(x:longint):longint; 14 | begin 15 | if home[x]=x then exit(x); 16 | home[x]:=find(home[x]); 17 | exit(home[x]); 18 | end; 19 | 20 | procedure link(x,y,z:longint); 21 | begin 22 | inc(cnt); e[cnt]:=y; w[cnt]:=z; 23 | next[cnt]:=head[x]; head[x]:=cnt; 24 | end; 25 | 26 | procedure sort(l,r: longint); 27 | var i,j,x,y: longint; 28 | begin 29 | i:=l; j:=r; 30 | x:=aw[(l+r) >>1]; 31 | repeat 32 | while aw[i]j) then 35 | begin 36 | y:=aw[i]; aw[i]:=aw[j]; aw[j]:=y; 37 | y:=ax[i]; ax[i]:=ax[j]; ax[j]:=y; 38 | y:=ay[i]; ay[i]:=ay[j]; ay[j]:=y; 39 | inc(i); j:=j-1; 40 | end; 41 | until i>j; 42 | if l0 do 79 | begin 80 | if not v[e[t]] then 81 | begin 82 | f[e[t],0]:=x; 83 | g[e[t],0]:=w[t]; 84 | h[e[t]]:=h[x]+1; 85 | dfs(e[t]); 86 | end; 87 | t:=next[t]; 88 | end; 89 | end; 90 | 91 | function lca(x,y:longint):longint; 92 | var i,t:longint; 93 | begin 94 | if h[x]f[y,i] then 102 | begin x:=f[x,i]; y:=f[y,i]; end; 103 | if x=y then exit(x); 104 | exit(f[x,0]); 105 | end; 106 | 107 | function ask(x,y:longint):longint; 108 | var i,hk,t:longint; 109 | begin 110 | hk:=maxlongint; 111 | t:=h[x]-h[y]; 112 | for i:=0 to 17 do 113 | if not ((1<find(tg) then writeln(-1) 139 | else begin 140 | t:=lca(sa,tg); 141 | writeln(min(ask(sa,t),ask(tg,t))); 142 | end; 143 | end; 144 | end. -------------------------------------------------------------------------------- /codes/luogu/luogu1967_truck.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 作者:CWW970329 3 | 题目:p3287 货车运输 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | const int INF=0x3f3f3f3f; 12 | const int N = 1e5 + 5; 13 | int n,m; 14 | 15 | struct gragh{ 16 | struct Edge{ 17 | int from,to,w; 18 | Edge(){} 19 | Edge(int x,int y,int z):from(x),to(y),w(z){} 20 | bool operator < (const Edge& a)const{ 21 | return w < a.w; 22 | } 23 | }edges[N],be[N]; 24 | int E,f[N],fa[N][20],di[N][20],dep[N]; 25 | bool vis[N]; 26 | vectorG[N]; 27 | int F(int x){//并查集 28 | return f[x]==x?x:(f[x]=F(f[x])); 29 | } 30 | 31 | inline void link(int x,int y,int z){ 32 | edges[++E]=Edge(x,y,z); 33 | G[x].push_back(E); 34 | } 35 | 36 | void build(){ 37 | E=0; 38 | for (int i=1;i<=n;i++)G[i].clear(); 39 | int x,y,z; 40 | for (int i=1;i<=n;i++)f[i]=i; 41 | for (int i=1;i<=m;i++){ 42 | scanf("%d%d%d",&x,&y,&z); 43 | be[i]=Edge(x,y,z); 44 | f[F(x)]=F(y); 45 | } 46 | } 47 | 48 | void kruskal(){ 49 | int treenum = 0;//forests 50 | memset(vis,0,sizeof(vis)); 51 | for (int i=1;i<=n;i++)if (!vis[F(i)]){ 52 | treenum++;vis[F(i)]=1; 53 | } 54 | for (int i=1;i<=n;i++)f[i]=i; 55 | sort(be+1,be+m+1); 56 | int cnt = 0; 57 | for (int i=m;i>=1;i--){ 58 | int x = be[i].from; 59 | int y = be[i].to ; 60 | if (F(x)==F(y))continue; 61 | f[F(x)]=F(y); 62 | cnt++; 63 | link(x,y,be[i].w); 64 | link(y,x,be[i].w); 65 | if (cnt==n-treenum)break; 66 | } 67 | } 68 | 69 | void dfs(int x){ 70 | vis[x] = 1; 71 | for (int i=1;i<=17;i++){ 72 | if(dep[x]<(1<=0;i--) 92 | if (fa[x][i]!=fa[y][i]){ 93 | x=fa[x][i];y=fa[y][i]; 94 | } 95 | if (x==y)return x; 96 | return fa[x][0]; 97 | } 98 | 99 | int ask(int x,int f){//f:father 100 | int ans = INF; 101 | int t = dep[x]-dep[f]; 102 | for (int i=0;i<=17;i++)if(t&(1<= s2 && s1 <= e2` 17 | 18 | 在涉及区间重叠的问题上,一般都会先进行排序。 19 | 20 | 最优化问题都可以通过某种搜索获得最优解,最多区间调度问题,其解空间为一棵二叉子集树,某个区间选或者不选构成了两个分支。 21 | 22 | 最大区间重叠又称最少工人调度,在有人数限制的情况下运用结束时间排序更好,相当于最多区间调度问题了。 23 | 24 | 维护每个工人的结束时间,优先选择结束时间晚的工人。 25 | 26 | 机器调度运用K叉树得到最优解,最长处理时间优先贪心只能近似解。 27 | 28 | - 已经证明,当机器数(或称工序数)m≥3时,流水作业调度问题是一个NP-hard问题。 29 | - 已经证明,机器调度问题是NP-hard问题(多机器并行) 30 | 31 | ## 人数限制为K的最多区间调度问题: 32 | 33 | ``` 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | using namespace std; 41 | typedef long long LL; 42 | const int maxn = 1e5 + 10; 43 | typedef pairP; 44 | P p[maxn]; 45 | LL a[maxn]; 46 | bool cmp(const P& x,const P& y){ 47 | if(x.second != y.second) return x.second < y.second; 48 | return x.first < y.first; 49 | } 50 | int main(){ 51 | LL n,k; 52 | multiset sets; 53 | while(scanf("%lld%lld",&n,&k) == 2){ 54 | sets.clear(); 55 | for(int i = 0;i < n;i++){ 56 | scanf("%lld%lld",&p[i].first,&p[i].second); 57 | } 58 | sort(p,p + n,cmp); 59 | multiset::iterator ite; 60 | int cnt = 0; 61 | while(k--) sets.insert(0); 62 | for(int i = 0; i < n; i++) { 63 | P point = p[i]; 64 | if(*sets.begin() > point.first) continue; 65 | if(*sets.begin() == point.first) { 66 | cnt++; 67 | sets.erase(sets.begin()); 68 | sets.insert(point.second); 69 | } else { 70 | ite = sets.upper_bound(point.first); 71 | ite--; 72 | cnt++; 73 | sets.erase(ite); 74 | sets.insert(point.second); 75 | } 76 | } 77 | cout << cnt << endl; 78 | } 79 | } 80 | ``` 81 | 82 | ## 尼克的任务 83 | 84 | 题目链接[luogu1280](https://www.luogu.org/problemnew/show/P1280) 85 | 86 | ### 题目描述 87 | 88 | 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成。 89 | 90 | 尼克的一个工作日为N分钟,从第一分钟开始到第N分钟结束。当尼克到达单位后他就开始干活。如果在同一时刻有多个任务需要完成,尼克可以任选其中的一个来做,而其余的则由他的同事完成,反之如果只有一个任务,则该任务必需由尼克去完成,假如某些任务开始时刻尼克正在工作,则这些任务也由尼克的同事完成。如果某任务于第P分钟开始,持续时间为T分钟,则该任务将在第P+T-1分钟结束。 91 | 92 | 写一个程序计算尼克应该如何选取任务,才能获得最大的空暇时间。 93 | 94 | ### 解题思路 95 | 96 | 如果改时间没有工作需要开始,那么当然是要休息的,表示现在休息一分钟,`dp[i]= dp[i+1]+1` 97 | 98 | ~~如果有需要开始的,那么在所有需要开始的工作中选一个最优的~~(废话)`+1`! 99 | 100 | 他工作完之后最多能休息多长时间,注意恰好工作完的那一分钟,即工作的最后一分钟,不算休息,这就是`+1`的原因。 101 | 102 | 然后解题用了逆向推,至于为什么除了不会正推也就没什么了。。。。(ಡωಡ)(ಡωಡ). 103 | 104 | 105 | ### AC代码 106 | 107 | ```c++ 108 | #include 109 | using namespace std; 110 | int ks[100005],js[100005],f[100005],N,K;//神奇的变量名模仿某人的。。。 111 | int main(){ 112 | //freopen("std.in","r",stdin); 113 | scanf("%d%d",&N,&K); 114 | for(int i=1;i<=K;i++){ 115 | int a,b; 116 | scanf("%d%d",&a,&b); 117 | ks[i]=a; 118 | js[i]=b; 119 | } 120 | int point=K; 121 | for(int i=N;i>=1;i--){ 122 | if(ks[point]!=i) 123 | f[i]=f[i+1]+1; 124 | else{ 125 | while(i==ks[point]){ 126 | f[i]=max(f[i],f[i+js[point]]); 127 | point--; 128 | } 129 | } 130 | } 131 | printf("%d\n",f[1]); 132 | return 0; 133 | } 134 | ``` 135 | -------------------------------------------------------------------------------- /codes/luogu/luogu4927.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | typedef long long LL; 4 | const int N = LL(1e5 + 7) << 2; 5 | const LL MOD = 998244353; 6 | 7 | LL sqr(LL x){return x*x;} 8 | LL gcd(LL x, LL y){ 9 | if (y) while((x %= y) && (y %= x)); 10 | return x + y; 11 | } 12 | LL power(LL a, LL x){ 13 | LL ans = 1; 14 | for (; x; x >>= 1){ 15 | if (x & 1) ans = (ans * a) % MOD;//不能直接乘 16 | a = (a * a) % MOD; 17 | } 18 | return ans % MOD; 19 | } 20 | 21 | LL arr[N]; 22 | 23 | struct SegTree{ 24 | #define lc (rt << 1) 25 | #define rc (rt << 1 | 1) 26 | #define lson rt<<1, l, mid 27 | #define rson rt<<1|1, mid+1, r 28 | 29 | LL n; 30 | // for one Node 31 | LL sum[N], len[N], tag[N]; 32 | LL len_2[N], len_sum[N]; // include child nodes 33 | LL con[N]; // contribution, also include child nodes 34 | 35 | // commonly pushUp 36 | void pushUp(const LL &rt) { 37 | sum[rt] = sum[lc] + sum[rc]; 38 | len_sum[rt] = len[rt]*sum[rt] + len_sum[lc] + len_sum[rc]; 39 | con[rt] = sqr(sum[rt]) + con[lc] + con[rc]; 40 | } 41 | 42 | void Tag(const LL &rt, const LL &l, const LL &r, const LL &v){ 43 | sum[rt] += v * (r-l+1); 44 | tag[rt] += v; 45 | con[rt] += 2*v*len_sum[rt] + sqr(v)*len_2[rt]; 46 | len_sum[rt] += v * len_2[rt]; 47 | } 48 | 49 | void pushDown(const LL &rt, const LL &l, const LL &r){ 50 | if (!tag[rt]) return; 51 | LL mid = (l+r) >> 1; 52 | Tag(lson, tag[rt]); 53 | Tag(rson, tag[rt]); 54 | tag[rt] = 0; 55 | } 56 | 57 | void build(const LL &rt, const LL &l, const LL &r) { 58 | if (l == r) { 59 | sum[rt] = len_sum[rt] = arr[l]; 60 | len[rt] = len_2[rt] = 1; 61 | tag[rt] = 0; 62 | con[rt] = sqr(sum[rt]); 63 | return; 64 | } 65 | LL mid = (l + r) >> 1; 66 | build(lson); 67 | build(rson); 68 | len[rt] = len[lc] + len[rc]; 69 | len_2[rt] = sqr(len[rt]) + len_2[lc] + len_2[rc]; 70 | tag[rt] = 0; 71 | pushUp(rt); 72 | } 73 | 74 | void update(const LL &rt, const LL &l, const LL &r, const LL &L, const LL &R, const LL &v) { 75 | if (L <= l && r <= R) { 76 | Tag(rt, l, r, v); 77 | return; 78 | } 79 | LL mid = (l + r) >> 1; 80 | pushDown(rt, l, r); 81 | if (L <= mid) update(lson, L, R, v); 82 | if (mid < R) update(rson, L, R, v); 83 | pushUp(rt); 84 | } 85 | 86 | void query(){ // prLL ans; 87 | LL p = con[1], q = sum[1], gd = gcd(p, q); 88 | p /= gd, q /= gd; 89 | //prLLf("%d, %d: ", p, q); 90 | LL ans = ((p%MOD) * power(q, MOD-2)) % MOD; 91 | printf("%lld\n", ans); 92 | } 93 | 94 | void prLL(LL rt, LL l, LL r){ // prLL tree 95 | printf("%d: [%d, %d], len = %d, len_2 = %d, sum = %d, len_sum = %d, tag = %d, con = %d\n", 96 | rt, l, r, len[rt], len_2[rt], sum[rt], len_sum[rt], tag[rt], con[rt]); 97 | if (l == r) return; 98 | LL mid = (l + r) >> 1; 99 | prLL(rt<<1, l, mid); 100 | prLL(rt<<1|1, mid+1, r); 101 | } 102 | } T; 103 | 104 | 105 | int main(){ 106 | //freopen("in.txt", "r", stdin); 107 | LL n, m; 108 | scanf("%lld%lld", &n, &m); 109 | for (LL i = 1; i <= n; i++){ 110 | scanf("%lld", &arr[i]); 111 | } 112 | T.build(1, 1, n); 113 | //T.print(1, 1, n); 114 | 115 | for (LL op, l, r, v; m--;){ 116 | scanf("%lld", &op); 117 | if (op==2) T.query(); 118 | else{ // update 119 | scanf("%lld%lld%lld", &l, &r, &v); 120 | T.update(1, 1, n, l, r, v); 121 | } 122 | } 123 | return 0; 124 | } -------------------------------------------------------------------------------- /13-DataStruct/树状数组与逆序对.md: -------------------------------------------------------------------------------- 1 | # 树状数组与逆序对 2 | 3 | 缺少一道模板题 4 | 5 | ## Agent2 6 | 7 | ### 题目描述 8 | 9 | 炎炎夏日还没有过去,Agent们没有一个想出去外面搞事情的。每当ENLIGHTENED总部组织活动时,人人都说有空,结果到了活动日,却一个接着一个咕咕咕了。只有不咕鸟Lyn_king一个人冒着太阳等了半个多小时,然后居然看到连ENLIGHTENED行动参谋都咕咕咕了,果然咕咕咕是人类的本性啊。 10 | 作为一个ENLIGHTENED行动指挥,自然不想看到这一点,于是他偷取到了那些经常咕咕咕的Agent的在下来NN天的活动安排表,并且叫上了你来整理。在整理过程中,ENLIGHTENED行动指挥对你说了MM条命令,命令操作如下。 11 | 输入0,a,b,这代表在第aa天到第bb天,有一名Agent要咕咕咕。 12 | 输入1 a,这代表ENLIGHTENED行动指挥询问你根据目前的信息,在第aa天有多少名Agent会咕咕咕。 13 | 作为同是不咕鸟的你,也想要惩戒那些经常咕咕咕的人,所以,请协助完成ENLIGHTENED行动指挥完成整理,并且在他每次询问时,输出正确的答案。 14 | 15 | 测试网站[p4939](https://www.luogu.org/problemnew/show/P4939) 16 | 17 | ### 题目分析 18 | 19 | 这道题思路树状数组+差分。 20 | 差分思想:假设有一个从小到大的数列a[] a 1 3 6 8 9 21 | 然后还有一个差分数组b[] b 1 2 3 2 1 22 | 我们可以看出,这里b[i]=a[i]-a[i-1],其中令b[1]=a[1]。 23 | 我们知道,树状数组对于单点值的修改十分方便,而对于区间的修改就比较尴尬,这时候就要用到差分。 24 | 还是上面的`a[]`和`b[]`,现在我们使区间`[2,4]`的所有数均`+2`,则`a[]`,`b[]`变为: 25 | a 1 5 8 10 9 26 | b 1 4 3 2 -1 27 | 事实上,这里只有b[2]和b[5]发生了变化,因为区间内元素均增加了同一个值,所以b[3],b[4]是不会变化的。 28 | 对于区间[x,y]的修改(增加值为d)在b数组内引起变化的只有 b[x]+=d,b[y+1]-=d。 29 | 而a[i] = b[1]+b[2]+...+b[i],所以建立在差分数组上的树状数组维护的前缀和是a数组中某个点的值。 30 | 具体实现见代码。 31 | 32 | ### 代码示例 33 | ```c++ 34 | #include 35 | #include 36 | #include 37 | using namespace std; 38 | #define N 10000010 39 | int c[N];//C[]为树状数组 40 | int n; 41 | int lowbit(int x){ 42 | return x&(-x); 43 | } 44 | void Insert(int i,int x){ 45 | while(i<=n) 46 | { 47 | c[i]+=x; 48 | i+=lowbit(i); 49 | } 50 | } 51 | int getsum(int i){//树状数组求和 52 | int sum=0; 53 | while(i>0) 54 | { 55 | sum+=c[i]; 56 | i-=lowbit(i); 57 | } 58 | return sum; 59 | } 60 | int main(){ 61 | int m, p; 62 | cin >> n >> m; 63 | memset(c,0,sizeof(c)); 64 | while(m --){ 65 | cin >> p; 66 | if(!p){ 67 | int a, b; 68 | cin >> a >> b; 69 | Insert(a, 1);//差分,把d[a]+1,同时d[b+1]-1 70 | Insert(b+1, -1); 71 | } 72 | if(p){ 73 | int a; 74 | cin >> a; 75 | cout << getsum(a) << endl; 76 | } 77 | } 78 | return 0; 79 | } 80 | ``` 81 | 82 | 83 | ## 逆序对 84 | 85 | ### 题目描述 86 | 87 | 测试网站[p1908](https://www.luogu.org/problemnew/show/P1908) 88 | 89 | 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中`a_i > a_j`且`i 104 | #include 105 | #include 106 | using namespace std; 107 | #define N 500010 108 | #define ll long long 109 | ll c[N];//C[]为树状数组 110 | int a[N], h[N];//a[]为读入的数组,h[]为要离散化后的数组 111 | int n; 112 | int lowbit(int x) 113 | { 114 | return x&(-x); 115 | } 116 | void Insert(int i,int x) 117 | { 118 | while(i<=n){ 119 | c[i]+=x; 120 | i+=lowbit(i); 121 | } 122 | } 123 | ll getsum(int i)//树状数组求和 124 | { 125 | ll sum=0; 126 | while(i>0){ 127 | sum+=c[i]; 128 | i-=lowbit(i); 129 | } 130 | return sum; 131 | } 132 | int main() 133 | { 134 | cin>>n; 135 | ll ans=0; 136 | memset(c,0,sizeof(c)); 137 | for(int i=1;i<=n;i++){ 138 | cin >> a[i]; 139 | h[i] = a[i]; 140 | } 141 | sort(h+1, h+n+1); 142 | int t = unique(h+1, h+n+1) - h;//离散化 143 | for(int i=1; i<=n; i++){ 144 | int k = lower_bound(h+1, h+t, a[i]) - h; 145 | Insert(k,1);//将当前元素插入树状数组,维护当前的状态 146 | ans+=i-getsum(k);//统计当前序列中大于a[i]的个数 147 | } 148 | cout< m 或者 u > n 的情况。 14 | - 请把 v > m 或者 u > n 的数据自觉过滤掉。 15 | 16 | 算法:二分图匹配 17 | 18 | ### 题目分析 19 | 20 | 二分图匹配的最大匹配问题,裸题。由于n,m都在1000以内,所以可以选用匈牙利算法(O(VE))。 21 | 22 | ###源程序 23 | 24 | ```c++ 25 | #include 26 | using namespace std; 27 | 28 | const int maxn = 1000 + 10; 29 | 30 | bool g[maxn][maxn];//存图,pXn 的图 31 | bool vis[maxn];//标记一次寻找增广路是对边顶点 32 | int line[maxn];//记录匹配方案 33 | //寻找增广路O(VE) 34 | bool dfs(int u,int n) 35 | { 36 | for(int v = 0; v < n; v++) 37 | { 38 | if(g[u][v] && !vis[v]) 39 | { 40 | vis[v] = true; 41 | if(line[v] == -1 || dfs(line[v],n)) 42 | {line[v] = u;return true;} 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | int Max_match(int n,int p) 49 | { 50 | int all = 0; 51 | memset(line,-1,sizeof(line)); 52 | for(int i = 0; i < p; i++) 53 | { 54 | memset(vis,false,sizeof(vis)); 55 | if(dfs(i,n)) all++; 56 | } 57 | return all; 58 | } 59 | 60 | int n,m,e; 61 | 62 | int main() 63 | { 64 | scanf("%d%d%d",&n,&m,&e); 65 | memset(g,false,sizeof(g)); 66 | for(int i = 0; i < e; i++) 67 | { 68 | int u,v; 69 | scanf("%d%d",&u,&v); 70 | if(u < 1 || u > n || v < 1 || v > m) continue; 71 | u--,v--; 72 | g[u][v] = true; 73 | } 74 | int ans = Max_match(m,n); 75 | printf("%d\n",ans); 76 | return 0; 77 | } 78 | ``` 79 | 80 | 81 | ## luoguP1129 [ZJOI2007]矩阵游戏 82 | 83 | 测试网站[luoguP1129](https://www.luogu.org/problemnew/show/P1129) 84 | 85 | ## 题目描述 86 | 87 | 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个N×N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作: 88 | 89 | 行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色) 90 | 91 | 列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色) 92 | 93 | 游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。 94 | 95 | 对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!于是小Q决定写一个程序来判断这些关卡是否有解。 96 | 97 | 输入格式 98 | 99 | 第一行包含一个整数T,表示数据的组数。 100 | 101 | 接下来包含T组数据,每组数据第一行为一个整数N,表示方阵的大小;接下来N行为一个N×N的01矩阵(0表示白色,1表示黑色)。 102 | 103 | 104 | ### 题目分析 105 | 106 | 二分图最大匹配变形,使用匈牙利算法。 107 | 行和列交换,问是否最终能使正对角线上的各自全为黑色,经过观察可以发现,对于任意2个黑色方块,如果它们初始状态时不在同一行(列),那么无论如何交换,它们都不会在同一行(列)。所以,只需要查看初始状态下,对于每一行是否有一列能与之匹配,再利用二分图最大匹配查看行和列的匹配数,如若等于n,则说明则可以满足题意。输出yes,否则输出No。 108 | 109 | ### 源程序 110 | 111 | ```c++ 112 | #include 113 | using namespace std; 114 | 115 | const int maxn = 200 + 10; 116 | const int maxm = 100000 + 10; 117 | 118 | bool vis[maxn]; 119 | int line[maxn]; 120 | int h[maxn]; 121 | int tot = 0; 122 | 123 | struct edge{ 124 | int to,next; 125 | edge(int to = 0, int next = 0) : to(to), next(next) {} 126 | }es[maxm]; 127 | 128 | void add_edge(int u, int v) 129 | { 130 | es[tot] = edge(v,h[u]); 131 | h[u] = tot++; 132 | } 133 | 134 | bool dfs(int u,int n) 135 | { 136 | for(int i = h[u]; ~i ; i = es[i].next) 137 | { 138 | int v = es[i].to; 139 | if(!vis[v]) 140 | { 141 | vis[v] = true; 142 | if(line[v] == -1 || dfs(line[v],n)) 143 | {line[v] = u;return true;} 144 | } 145 | } 146 | return false; 147 | } 148 | 149 | int Max_match(int n,int p) 150 | { 151 | int all = 0; 152 | memset(line,-1,sizeof(line)); 153 | for(int i = 1; i <= p; i++) 154 | { 155 | memset(vis,false,sizeof(vis)); 156 | if(dfs(i,n)) all++; 157 | else break; 158 | } 159 | return all; 160 | } 161 | 162 | int n; 163 | 164 | int main() 165 | { 166 | int T; 167 | scanf("%d",&T); 168 | while(T--) 169 | { 170 | tot = 0; 171 | scanf("%d",&n); 172 | memset(h,-1,sizeof(h)); 173 | for(int i = 1; i <= n; i++) 174 | for(int j = 1; j <= n; j++) 175 | { 176 | int x; 177 | scanf("%d",&x); 178 | if(x == 1) 179 | add_edge(i,j); 180 | 181 | } 182 | int ans = Max_match(n,n); 183 | if(ans == n) 184 | printf("Yes\n"); 185 | else printf("No\n"); 186 | } 187 | return 0; 188 | } 189 | 190 | ``` 191 | 192 | 193 | ## luoguP1640 [SCOI2010]连续攻击游戏 194 | 195 | ### 题目描述 196 | 197 | 测试网站[luoguP1640](https://www.luogu.org/problemnew/show/P1640) 198 | 199 | lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。现在lxhgww想知道他最多能连续攻击boss多少次? 200 | 201 | ### 题目分析 202 | 203 | 二分图最大匹配变形,使用匈牙利算法,左边为武器属性,右边为武器编号,根据武器的属性进行建边,然后从1到属性最大值开始找增广路,若没找到则break,因为武器属性必须连续。 204 | 205 | ### 源程序 206 | 207 | ```c++ 208 | #include 209 | using namespace std; 210 | 211 | const int maxn = 1000000 + 10; 212 | 213 | bool vis[maxn]; 214 | int line[maxn]; 215 | int h[maxn]; 216 | int tot = 0; 217 | 218 | struct edge{ 219 | int to,next; 220 | edge(int to = 0, int next = 0) : to(to), next(next) {} 221 | }es[maxn*2]; 222 | 223 | void add_edge(int u, int v){ 224 | es[tot] = edge(v,h[u]); 225 | h[u] = tot++; 226 | } 227 | 228 | bool dfs(int u,int n){ 229 | for(int i = h[u]; ~i ; i = es[i].next){ 230 | int v = es[i].to; 231 | if(!vis[v]) 232 | { 233 | vis[v] = true; 234 | if(line[v] == -1 || dfs(line[v],n)) 235 | {line[v] = u;return true;} 236 | } 237 | } 238 | return false; 239 | } 240 | 241 | int Max_match(int n,int p){ 242 | int all = 0; 243 | memset(line,-1,sizeof(line)); 244 | for(int i = 1; i < p; i++)//属性从1开始 245 | { 246 | memset(vis,false,sizeof(vis)); 247 | if(dfs(i,n)) all++; 248 | else break; 249 | } 250 | return all; 251 | } 252 | 253 | int n; 254 | 255 | int main(){ 256 | scanf("%d",&n); 257 | memset(h,-1,sizeof(h)); 258 | for(int i = 0; i < n; i++) 259 | { 260 | int a,b; 261 | scanf("%d%d",&a,&b); 262 | add_edge(a,i);add_edge(b,i); 263 | } 264 | int ans = Max_match(n,10001); 265 | printf("%d\n",ans); 266 | return 0; 267 | } 268 | ``` 269 | -------------------------------------------------------------------------------- /18-string2/回文树专题练习.md: -------------------------------------------------------------------------------- 1 | # 回文树专题练习 2 | 3 | // 这里可以用一小段话谈一谈~~人生和理想~~知识点总结,模板 4 | 5 | ## Not common palindromes 6 | 7 | 测试网站[URAL-2059](https://vjudge.net/problem/URAL-2059) 8 | 9 | ### 题目翻译 10 | 11 | 你被要求求出3个数字: 12 | 1、非空回文串P的数量,P在A中出现的次数比在B中多; 13 | 2、非空回文串P的数量,P在A中出现的次数与在B中一样多; 14 | 3、非空回文串P的数量,P在A中出现的次数比在B中少; 15 | 16 | ### 题目分析 17 | 18 | 这道题要求出两个字符串中回文串的数量,并统计回文串在A中出现次数比B中多的、一样多的、少的,数量各有多少。对两个字符串分别先建它们的回文树,得到每个回文子串出现的次数,然后分别找两个字符串都有的偶数长度的回文子串和奇数长度的回文子串,统计A比B多的为ans0,跟B一样多的 19 | 为ans1,比B少的为ans2,而A比B多的还应当包括A有B没有的那部分,即为A的回文子串总数减去AB都有的,同理B也一样。具体实现见代码。 20 | 21 | ### 代码示例 22 | 23 | ```c++ 24 | #include 25 | using namespace std; 26 | #define ll long long 27 | const int MAXN = 300005 ; 28 | const int N = 26 ; 29 | struct Palindromic_Tree { 30 | int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 31 | int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点 32 | int cnt[MAXN] ;//记录节点代表的回文串的次数 33 | int len[MAXN] ;//len[i]表示节点i表示的回文串的长度 34 | int S[MAXN] ;//存放添加的字符 35 | int last ;//指向上一个字符所在的节点,方便下一次add 36 | int n ;//字符数组指针 37 | int p ;//节点指针 38 | int newnode ( int l ) {//新建节点 39 | for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ; 40 | cnt[p] = 0 ; 41 | len[p] = l ; 42 | return p ++ ; 43 | } 44 | void init () {//初始化 45 | p = 0 ; 46 | newnode ( 0 ) ; 47 | newnode ( -1 ) ; 48 | last = 0 ; 49 | n = 0 ; 50 | S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判 51 | fail[0] = 1 ; 52 | } 53 | int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的 54 | while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; 55 | return x ; 56 | } 57 | void add ( int c ) { 58 | S[++ n] = c ; 59 | int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置 60 | if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 61 | int now = newnode ( len[cur] + 2 ) ;//新建节点 62 | fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转 63 | next[cur][c] = now ; 64 | } 65 | last = next[cur][c] ; 66 | cnt[last] ++ ; 67 | } 68 | void count () { 69 | for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ; 70 | //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串! 71 | } 72 | } ; 73 | string str1, str2; 74 | int ans0, ans1, ans2; 75 | Palindromic_Tree pa, pb; 76 | void init() { 77 | ans0 = 0; 78 | ans1 = 0; 79 | ans2 = 0; 80 | } 81 | void input() { 82 | cin >> str1 >> str2; 83 | } 84 | void dfs(int x, int y) { 85 | if(x > 1) { 86 | if(pa.cnt[x] > pb.cnt[y])ans0 ++; 87 | else if(pa.cnt[x] == pb.cnt[y])ans1 ++; 88 | else ans2 ++; 89 | } 90 | for(int i = 0; i < N; i++) { 91 | if(pa.next[x][i] && pb.next[y][i]) { 92 | dfs(pa.next[x][i], pb.next[y][i]); 93 | } 94 | } 95 | } 96 | void solve() { 97 | //获取pa的回文串 98 | pa.init(); 99 | for(int i = 0; i < str1.size(); i++) { 100 | pa.add(str1[i] - 'a'); 101 | } 102 | pa.count(); 103 | // 获取pb的回文串 104 | pb.init(); 105 | for(int i = 0; i < str2.size(); i++) { 106 | pb.add(str2[i] - 'a'); 107 | } 108 | pb.count(); 109 | int ans = 0; 110 | dfs(0, 0);//从偶数节点开始找a,b都有的回文子串 111 | dfs(1, 1);//从奇数字节开始找a,b都有的回文子串 112 | ans = ans0 + ans1 + ans2;//a、b都有的回文串总数 113 | ans0 += pa.p - 2 - ans;//a比b多的回文串子串包括b没有的回文子串 114 | ans2 += pb.p - 2 - ans;//b比a多的回文串子串包括a没有的回文子串 115 | } 116 | 117 | void output(int k) { 118 | printf("Case #%d: %d %d %d\n", k, ans0, ans1, ans2); 119 | } 120 | 121 | int main() { 122 | int ca; 123 | int k = 0; 124 | cin >> ca; 125 | while(ca --) { 126 | k ++; 127 | init(); 128 | input(); 129 | solve(); 130 | output(k); 131 | } 132 | 133 | } 134 | ``` 135 | 136 | 137 | ## The Number of Palindromes 138 | 139 | 测试网站[HUD3948](https://vjudge.net/problem/HDU-3948) 140 | 141 | ### 题目翻译 142 | 求字符串S的不同回文串的个数。 143 | 144 | ### 题目分析 145 | 这道题要求一个字符串不同性质的回文串的个数,不同性质包括字符不同或长度不同,这道题用manacher算法会比较困难,而回文树就可以轻松解决。回文树的每个节点就代表一个不同性质的回文串,所以只要将回文树建好,然后输出回文树节点数-2即可,减去两个根节点。具体实现见代码。 146 | 147 | ### 代码示例 148 | 149 | ```c++ 150 | #include 151 | using namespace std; 152 | #define ll long long 153 | const int MAXN = 100005 ; 154 | const int N = 26 ; 155 | int ans = 0; 156 | struct Palindromic_Tree { 157 | int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 158 | int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点 159 | int cnt[MAXN] ;//存储该节点表示的回文串出现的次数 160 | // int num[MAXN] ; 161 | int len[MAXN] ;//len[i]表示节点i表示的回文串的长度 162 | int S[MAXN] ;//存放添加的字符 163 | int last ;//指向上一个字符所在的节点,方便下一次add 164 | int n ;//字符数组指针 165 | int p ;//节点指针 166 | int newnode ( int l ) {//新建节点 167 | for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ; 168 | cnt[p] = 0 ; 169 | // num[p] = 0 ; 170 | len[p] = l ; 171 | //cout << p << ' '; 172 | return p ++ ; 173 | } 174 | void init () {//初始化 175 | p = 0 ; 176 | newnode ( 0 ) ; 177 | newnode ( -1 ) ; 178 | last = 0 ; 179 | n = 0 ; 180 | S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判 181 | fail[0] = 1 ; 182 | ans = 0; 183 | } 184 | int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的 185 | while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; 186 | return x ; 187 | } 188 | void add ( int c ) { 189 | S[++ n] = c ; 190 | int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置 191 | if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 192 | int now = newnode ( len[cur] + 2 ) ;//新建节点 193 | fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转 194 | next[cur][c] = now ; 195 | } 196 | last = next[cur][c] ; 197 | cnt[last] ++ ; 198 | } 199 | } ; 200 | string str; 201 | Palindromic_Tree p; 202 | int main() { 203 | int ca; 204 | int k = 0; 205 | cin >> ca; 206 | while(ca --) { 207 | k ++; 208 | cin >> str; 209 | p.init(); 210 | for(int i=0; i 23 | using namespace std; 24 | const int N = 333; 25 | 26 | bool ok, vis[N]; 27 | int n, a[N][N], s, t, top, b[N]; 28 | 29 | void dfs(int u, int dep){ 30 | if (ok) return; 31 | vis[u] = true; 32 | b[dep] = u; 33 | if (u==t){ 34 | ok = true; 35 | top = dep; 36 | return; 37 | } 38 | 39 | for (int i = 1; i <= n; i++){ 40 | if (a[u][i] && (!vis[i])){ 41 | dfs(i, dep+1); 42 | } 43 | } 44 | } 45 | 46 | void printAns(){ 47 | for (int i = 1; i <= top; i++){ 48 | printf("%d%c", b[i], i==n ? '\n' : ' '); 49 | } 50 | } 51 | 52 | int main(){ 53 | ///freopen("in.txt", "r", stdin); 54 | scanf("%d", &n); 55 | for (int i = 1; i <= n; i++){ 56 | for (int j = 1; j <= n; j++){ 57 | scanf("%d", &a[i][j]); 58 | } 59 | } 60 | 61 | scanf("%d%d", &s, &t); 62 | ok = false; 63 | memset(vis, false, sizeof vis); 64 | dfs(s, 1); 65 | if (ok) printAns(); 66 | else puts("-1"); 67 | return 0; 68 | } 69 | ``` 70 | 71 | ## 拓扑排序 72 | 73 | ```c++ 74 | #include 75 | using namespace std; 76 | const int N = 33; 77 | 78 | int n, vis[N], ind[N]; 79 | vector G[N], ans; 80 | bool loop[N]; 81 | 82 | void init(const int &n){ 83 | for (int i = 0; i <= n; i++) G[i].clear(); 84 | memset(ind, 0, sizeof ind); 85 | memset(loop, 0, sizeof loop); 86 | } 87 | 88 | void addEdge(const int &f, const int &t){ 89 | G[f].push_back(t); 90 | ind[t]++; 91 | } 92 | 93 | int get_start(){ 94 | for (int i = 1; i <= n; i++){ 95 | if (!ind[i]) return i; 96 | } 97 | return -1; 98 | } 99 | 100 | bool dfs(int u){ // return 0 if there is a loop 101 | if (loop[u]) return 0; 102 | vis[u] = 1; 103 | loop[u] = 1; 104 | 105 | for (int i = 0; i < G[u].size(); i++){ 106 | int v = G[u][i]; 107 | if (vis[v]) continue; 108 | if (!dfs(v)) return 0; 109 | } 110 | ans.push_back(u); 111 | loop[u] = 0; 112 | return 1; 113 | } 114 | 115 | void print_top_seq(){ 116 | for (int i = 0; i < n; i++){ 117 | printf("%c", ans[i] + 64); 118 | } 119 | puts("."); 120 | } 121 | 122 | int main() 123 | { 124 | //freopen("in.txt", "r", stdin); 125 | int t1, t2, lz, m; 126 | char ch[90][30]; 127 | while(scanf("%d:", &lz) == 1){ 128 | printf("%d:\n", lz); 129 | scanf("%d %d",&n,&m); 130 | init(n); 131 | for(int i = 1; i <= m; i++) scanf("%s", ch[i]); 132 | 133 | for(int i = 1; i <= m+1; i++){ 134 | if (i == m+1){ 135 | printf("Sorted sequence cannot be determined.\n"); 136 | break; 137 | } 138 | 139 | t1 = ch[i][0] - 'A' + 1; 140 | t2 = ch[i][2] - 'A' + 1; 141 | addEdge(t1, t2); 142 | 143 | int start = get_start(); 144 | ans.clear(); 145 | printf("start = %d\n", start); 146 | if (start == -1 || !dfs(start)){ 147 | printf("Inconsistency found after %d relations.\n", i); 148 | break; 149 | } else if (ans.size() == n){ 150 | printf("Sorted sequence determined after %d relations: ", i); 151 | print_top_seq(); 152 | break; 153 | } 154 | } 155 | } 156 | 157 | return 0; 158 | } 159 | ``` 160 | 161 | ## 神经网络 162 | 163 | 测试网站[P1038](https://www.luogu.org/problemnew/show/P1038) 164 | 165 | ### 题目描述 166 | 167 | 人工神经网络(Artificial Neural Network)是一种新兴的具有自我学习能力的计算系统,在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。对神经网络的研究一直是当今的热门方向,兰兰同学在自学了一本神经网络的入门书籍后,提出了一个简化模型,他希望你能帮助他用程序检验这个神经网络模型的实用性。 168 | 在兰兰的模型中,神经网络就是一张有向图,图中的节点称为神经元,而且两个神经元之间至多有一条边相连,下图是一个神经元的例子: 169 | ![](images/p1038_1.png) 170 | 神经元〔编号为1) 171 | 图中,X1-X3是信息输入渠道,Y1-Y2是信息输出渠道,C1表示神经元目前的状态,Ui是阈值,可视为神经元的一个内在参数。 172 | 神经元按一定的顺序排列,构成整个神经网络。在兰兰的模型之中,神经网络中的神经元分为几层;称为输入层、输出层,和若干个中间层。每层神经元只向下一层的神经元输出信息,只从上一层神经元接受信息。下图是一个简单的三层神经网络的例子。 173 | ![](images/p1038_2.png) 174 | 兰兰规定,Ci服从公式:(其中nn是网络中所有神经元的数目) 175 | ![](images/p1038_3.png) 176 | 公式中的Wji(可能为负值)表示连接j号神经元和i号神经元的边的权值。当 Ci大于00时,该神经元处于兴奋状态,否则就处于平静状态。当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号,信号的强度为Ci 177 | 如此.在输入层神经元被激发之后,整个网络系统就在信息传输的推动下进行运作。现在,给定一个神经网络,及当前输入层神经元的状态(Ci),要求你的程序运算出最后网络输出层的状态。 178 | 179 | 180 | 181 | ### 题目分析 182 | 183 | 题目要求输出层的状态并输出大于0的,而根据求状态的公式可以知道求当前神经元必须先求出上一层神经元的状态, 184 | 所以可以想到用bfs或拓扑排序模拟求状态的过程。这个题有一个坑:当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号。就是说在求当前神经元状态时要判断它的前驱神经元是否大于0。 185 | 这里用拓扑排序实现的,具体做法就是找到入度为0的点,放入队列,并计算其状态,算完后将与其连接的点的入度减一,直到队列为空。 186 | 187 | ### 代码示例 188 | 189 | ```c++ 190 | #include 191 | using namespace std; 192 | int n, p; 193 | int c[101],u[101], num[101][101], w[101][101]; 194 | int in_degree[101], out_degree[101]; 195 | queueq; 196 | void tp_sort(){//拓扑排序 197 | while (!q.empty()){ 198 | int now = q.front();//取出队列首的节点 199 | q.pop(); 200 | for (int i=1; i<=n; i++){ 201 | if(num[now][i] && c[now]>0){//如果该节点状态大于0则向下传递 202 | c[i] += c[now]*w[now][i]; 203 | } 204 | --in_degree[i];//将与其连接的节点的入度减一 205 | if (in_degree[i]==0)//如果其入度为0,将其放入队列 206 | q.push(i); 207 | } 208 | } 209 | } 210 | int main(){ 211 | cin>>n>>p; 212 | for (int i=1;i<=n;i++) 213 | cin>>c[i]>>u[i]; 214 | for (int i=1;i<=p;i++){ 215 | int u, v, c; 216 | cin>>u>>v>>c; 217 | num[u][v] = 1;//建邻接矩阵 218 | w[u][v] = c;//保存边权 219 | in_degree[v] ++;//入度 220 | out_degree[u] ++;//出度 221 | } 222 | for (int i=1;i<=n;i++) 223 | if (in_degree[i]==0)//将输入层放入队列 224 | q.push(i); 225 | else 226 | c[i]-=u[i];//可以先减去阈值 227 | tp_sort(); 228 | bool fg=0; 229 | for(int i=1; i<=n; i++) 230 | if (c[i] > 0 && out_degree[i] == 0){//输出出度为0的点即输出层的点 231 | fg=1; 232 | cout << i << " " << c[i] << endl;; 233 | } 234 | if (!fg) printf("NULL");//如果没有神经元的状态大于0则输出NULL 235 | return 0; 236 | } 237 | ``` -------------------------------------------------------------------------------- /12-dp1/背包2.md: -------------------------------------------------------------------------------- 1 | # 背包2 2 | 3 | ## HDU2955 01 背包 Robberies 4 | 5 | ### 题目描述 6 | 7 | 测试网站[HDU2955](http://acm.hdu.edu.cn/showproblem.php?pid=2955) 8 | 9 | Roy想要抢劫银行,每家银行都有一定的金额和被抓到的概率,知道Roy被抓的最大概率P,求Roy在不被抓的情况下,最多抢劫多少钱。 10 | 11 | ### 题目分析 12 | 13 | 到达性dp问题。 14 | 状态转移方程为 `dp[j] = max(dp[j],dp[j-v[i]] * (1-p[i]));`, `dp[i]` 表示能抢到`i`个百万不被抓到的最大概率。 15 | 套用01背包模板,把银行里的总钱数当作总容量,概率当作价值 ,最后再根据概率判断其能不能到达该钱数。 16 | 17 | `被抓到的最小概率 = 1-不被抓到的最大概率` 18 | 19 | ### 代码示例 20 | 21 | ```c++ 22 | #include 23 | 24 | using namespace std; 25 | const int maxn = 110; 26 | const int INF = 0x3f3f3f3f; 27 | int T; 28 | int n,k; 29 | double P; 30 | double p[maxn]; 31 | int v[maxn]; 32 | double dp[10005]; //dp数组,dp[i] 代表能抢到i个百万不被抓到的最大概率 33 | 34 | int main(){ 35 | int T; 36 | scanf("%d",&T); 37 | while(T--){ 38 | scanf("%lf%d",&P,&n); 39 | int sum = 0; 40 | for(int i = 0; i < n; i++) scanf("%d%lf",&v[i],&p[i]), sum += v[i]; //统计总钱数 41 | for(int i = 0; i <= sum; i++) dp[i] = 0; //初始化dp 数组 42 | dp[0] = 1; //抢到0个百万的概率总是为1 43 | for(int i = 0; i < n; i++) 44 | for(int j = sum; j-v[i] >= 0; j--) 45 | dp[j] = max(dp[j],dp[j-v[i]] * (1-p[i])); //状态转移 46 | int ans; 47 | for(int i = sum; i >= 0; i--) 48 | if(1-dp[i] <= P) //被抓到的最小概率 = 1-不被抓到的最大概率 49 | {ans = i;break;} 50 | printf("%d\n",ans); 51 | } 52 | return 0; 53 | } 54 | ``` 55 | 56 | 57 | ## 金明的预算方案 58 | 59 | [luogu1064](https://www.luogu.org/problemnew/show/P1064) 60 | 61 | 此题弱化版[开心的今明]()https://www.luogu.org/problemnew/show/P1060 62 | 63 | ### 题意 64 | 65 | 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子: 66 | 67 | 主件 附件 68 | 69 | 电脑 打印机,扫描仪 70 | 71 | 书柜 图书 72 | 73 | 书桌 台灯,文具 74 | 75 | 工作椅 无 76 | 77 | 如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。 78 | 但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1-5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 79 | 设第j件物品的价格为v[j], 重要度为w[j], 共选中了k件物品,编号一次为j1,j2,...jk,则所求总和为:v[j1] * w[j1] + v[j2] * w[j2] + ... + v[jk] * w[jk]。 80 | 请你帮助金明设计一个满足要求的购物单。 81 | 82 | 输入: 83 | 第1行,为两个正整数,用一个空格隔开: 84 | N m (其中 N(<32000) 表示总钱数,m(<60)为希望购买物品的个数。) 从第2行到第m+1行,第j行给出了编号为j−1的物品的基本数据,每行有3个非负整数 vpq (其中vv表示该物品的价格(v<10000),p表示该物品的重要度(1-5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号) 85 | 输出: 86 | 一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)。 87 | ### 思路 88 | 01背包模板题目,价格作为限制条件,等同于背包问题的体积,价格和重要度的乘积作为价值衡量,然后利用套用01背包模板解题。 89 | 然后这题是P1060的升级版,要分类讨论一下。 90 | 1.不取该主件 91 | 2.只取该主件 92 | 3.取主件和附件1 93 | 4.取主件和附件2 94 | 5.取主件和附件1、2 95 | 96 | ### 代码 97 | 98 | ```c++ 99 | #include 100 | 101 | using namespace std; 102 | const int maxc = 32000 + 10; 103 | const int maxn = 100 + 10; 104 | typedef pair P; 105 | 106 | int c,m,u,p,q,tot = 0; 107 | map mp; 108 | vector

vs[maxn]; 109 | int dp[maxc]; 110 | int main() 111 | { 112 | scanf("%d%d",&c,&m); 113 | for(int i = 1; i <= m; i++) 114 | { 115 | scanf("%d%d%d",&u,&p,&q); 116 | if(q == 0) 117 | { 118 | mp[i] = ++tot; 119 | vs[tot].push_back(P(u,u*p)); 120 | } 121 | else vs[mp[q]].push_back(P(u,u*p)); 122 | } 123 | for(int i = 1; i <= tot; i++) 124 | for(int j = c; j >= vs[i][0].first; j--) 125 | { 126 | dp[j] = max(dp[j],dp[j - vs[i][0].first] + vs[i][0].second); 127 | if(vs[i].size() >= 2 && j - vs[i][0].first - vs[i][1].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][1].first] + vs[i][0].second + vs[i][1].second); 128 | if(vs[i].size() >= 3 && j - vs[i][0].first - vs[i][2].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][2].first] + vs[i][0].second + vs[i][2].second); 129 | if(vs[i].size() >= 3 && j - vs[i][0].first - vs[i][1].first - vs[i][2].first >= 0) dp[j] = max(dp[j],dp[j - vs[i][0].first - vs[i][1].first - vs[i][2].first] + vs[i][0].second + vs[i][1].second + vs[i][2].second); 130 | } 131 | printf("%d\n",dp[c]); 132 | return 0; 133 | } 134 | 135 | ``` 136 | 137 | ## HDU5890 Eighty seven 138 | 139 | 测试网站[HDU5890](http://acm.hdu.edu.cn/showproblem.php?pid=5890) 140 | 141 | ### 题目描述 142 | 143 | Fib先生是一所小学的数学老师。 144 | 145 | 在下一课中,他计划教孩子们如何加数字。 146 | 147 | 在课前,他将准备N张数字牌。第i张牌上的号码是ai。 148 | 149 | 在课堂上,每回合,他将删除不超过3张卡片,然后让学生选择任意十张卡片,其中数字的总和为87. 150 | 151 | 每次转动后,被删除的卡片将被放回原位。现在,他想知道每回合是否至少有一个解决方案。你能帮助他吗? 152 | 153 | ### 题目分析 154 | 155 | 题目大意是 给出`n`个数和`q`次询问,每次删除`1`到`3`个数,问使否能从剩下的数中选`10`个数和为`87` 156 | 157 | 因为N只有50所以可以先预处理所有情况 158 | 159 | 然后用`bitset <90> dp[11]` 进行二维背包dp, `dp[i][j]` 为`1` 代表在取`i`个数时能加和到 `j` ; 160 | 161 | [bitset 用法](https://blog.csdn.net/snowy_smile/article/details/79120063) 162 | 163 | ### 代码示例 164 | 165 | ```c++ 166 | #include 167 | #include 168 | #include 169 | #include 170 | #include 171 | #include 172 | #include 173 | using namespace std; 174 | const int INF = 0x3f3f3f3f; 175 | const int maxn = 508 + 10; 176 | const int maxm = 10000 + 10; 177 | typedef pair P; 178 | typedef long long LL; 179 | int n,Q; 180 | int a[maxn]; 181 | bitset<90> dp[11]; // dp[i][j] 为1 代表在取i个数时能加和到 j 182 | bool ans[55][55][55]; // ans[i][j][k]表示然后i,j,k后是否能组成87 183 | bool check(int x,int y, int z) 184 | { 185 | for(int i = 0; i < 11; i++) 186 | dp[i].reset(); 187 | dp[0][0] = 1; 188 | for(int i = 1; i <= n; i++) 189 | { 190 | if(i != x && i != y && i != z) 191 | for(int j = 9; j >= 0; j--) 192 | dp[j+1] |= (dp[j] << a[i]);//二维dp + bitset优化 193 | } 194 | if(dp[10][87] == 1) return true; 195 | else return false; 196 | } 197 | 198 | int main(){ 199 | int T; 200 | scanf("%d",&T); 201 | while(T--) 202 | { 203 | scanf("%d",&n); 204 | for(int i = 1; i <= n; i++) scanf("%d",&a[i]); 205 | memset(ans,false,sizeof(ans)); 206 | //预处理 207 | for(int i = 1; i <= n; i++) 208 | for(int j = i; j <= n; j++) 209 | for(int k = j; k <= n; k++) 210 | { 211 | if(check(i,j,k)) 212 | { 213 | ans[i][j][k] = ans[i][k][j] = true; 214 | ans[j][i][k] = ans[j][k][i] = true; 215 | ans[k][i][j] = ans[k][j][i] = true; 216 | } 217 | } 218 | scanf("%d",&Q); 219 | while(Q--) 220 | { 221 | int i,j,k; 222 | scanf("%d%d%d",&i,&j,&k); 223 | if(ans[i][j][k]) 224 | printf("Yes\n"); 225 | else printf("No\n"); 226 | } 227 | } 228 | return 0; 229 | } 230 | ``` 231 | -------------------------------------------------------------------------------- /05-greedy/贪心算法练习.md: -------------------------------------------------------------------------------- 1 | # 贪心算法练习 2 | 3 | ## 题目一:国王游戏 4 | 5 | [洛谷 1080](https://www.luogu.org/problemnew/show/P1080) 6 | 7 | ### 题意 8 | 9 | 如题 10 | 11 | ### 思路 12 | 13 | 其实我们发现只需要考虑相邻两个数如何交换才是最优的,因为任意排列都可以由交换相邻两个数得到。 14 | 15 | 假设现在有两个大臣,国王左手为`a0`,右手为`b0`,大臣`1`左手为`a1`,右手为`b1`,大臣`2`左手为`a2`,右手为`b2`. 16 | 17 | 有两种排列方法: 18 | 19 | 1. 大臣`1`在前,最后答案为`max(a0/b1,a0*a1/b2)` 20 | 2. 大臣`2`在前,最后答案为`max(a0/b2,a0*a2/b1)` 21 | 22 | 不妨设大臣`1`在前为最后答案,题目要求最大值最小,那么就说明第一个最大值小于第二个最大值 23 | 24 | 又因为`a0/b1 37 | using namespace std; 38 | #define mst(a,b) memset((a),(b),sizeof(a)) 39 | #define rush() int T;scanf("%d",&T);while(T--) 40 | 41 | typedef long long ll; 42 | const int maxn = 1105; 43 | const ll INF = 1e18; 44 | const ll mod=1e9+7; 45 | const double eps = 1e-9; 46 | 47 | int n; 48 | int num[5005]; 49 | 50 | struct node 51 | { 52 | int x,y; 53 | }a[maxn]; 54 | 55 | bool cmp(node a,node b) 56 | { 57 | return a.x*a.y=0;i--) 75 | { 76 | flag=num[i]+flag*10000; 77 | num[i]=flag/x; 78 | flag%=x; 79 | } 80 | } 81 | 82 | int main() 83 | { 84 | int xx,yy; 85 | scanf("%d",&n); 86 | scanf("%d%d",&xx,&yy); 87 | for(int i=0;i=0&&num[now]==0) now--; 97 | if(now==-1) return puts("1"),0; 98 | printf("%d",num[now--]); 99 | while(now>=0) 100 | { 101 | printf("%04d",num[now--]); 102 | } 103 | puts(""); 104 | } 105 | 106 | ``` 107 | 108 | 109 | ## 题目二:均分纸牌 110 | 111 | [洛谷 1031](https://www.luogu.org/problemnew/show/P1031) 112 | 113 | ### 题意 114 | 115 | 如题 116 | 117 | ### 思路 118 | 119 | 因为牌的总张数是堆的倍数,所以排好序后每队的张数就是总张数的每堆平均数(总张数÷堆数),则只需模拟一下移动的过程即可: 120 | 121 | 从前往后扫描数组,判断距离平均数还差几张,如果小于平均数,则用后面那张补过来,如果大于平均数,则往后补 122 | 123 | 124 | ### 代码 125 | 126 | ```c++ 127 | #include 128 | using namespace std; 129 | #define mst(a,b) memset((a),(b),sizeof(a)) 130 | #define rush() int T;scanf("%d",&T);while(T--) 131 | 132 | typedef long long ll; 133 | const int maxn = 1105; 134 | const ll INF = 1e18; 135 | const ll mod=1e9+7; 136 | const double eps = 1e-9; 137 | 138 | int n; 139 | int a[maxn]; 140 | 141 | int main() 142 | { 143 | scanf("%d",&n); 144 | int ans=0; 145 | int sum=0; 146 | for(int i=0;isum) 151 | { 152 | a[i+1]+=(a[i]-sum); 153 | ans++; 154 | } 155 | if(a[i] 190 | using namespace std; 191 | #define mst(a,b) memset((a),(b),sizeof(a)) 192 | #define rush() int T;scanf("%d",&T);while(T--) 193 | 194 | typedef long long ll; 195 | const int maxn = 5105; 196 | const ll INF = 1e18; 197 | const ll mod=1e9+7; 198 | const double eps = 1e-9; 199 | 200 | int n; 201 | int vis[maxn]; 202 | struct node 203 | { 204 | int x,y; 205 | }a[maxn]; 206 | 207 | bool cmp(node a,node b) 208 | { 209 | if(a.x==b.x) return a.y>b.y; 210 | return a.x>b.x; 211 | } 212 | 213 | int main(){ 214 | scanf("%d",&n); 215 | for(int i=0;ibi,aj>bj时,bi>=bj,应该按b降序排序 280 | 281 | 那么对于不同组的怎么办,我们可以发现`1`组在`2`组前肯定能保证满足条件,`2`组在`3`组前面肯定能保证满足条件,那么就按照`1,2,3`的顺序排即可。 282 | 283 | ### 代码 284 | 285 | ```c++ 286 | #include 287 | using namespace std; 288 | #define mst(a,b) memset((a),(b),sizeof(a)) 289 | #define rush() int T;scanf("%d",&T);while(T--) 290 | 291 | typedef long long ll; 292 | const int maxn = 20005; 293 | const ll INF = 1e18; 294 | const ll mod=1e9+7; 295 | const double eps = 1e-9; 296 | 297 | int n; 298 | ll ans[maxn]; 299 | struct node 300 | { 301 | int x,y,d; 302 | }a[maxn]; 303 | 304 | bool cmp(node a,node b) 305 | { 306 | if(a.d!=b.d) return a.db.y; 309 | } 310 | 311 | int main() 312 | { 313 | rush() 314 | { 315 | scanf("%d",&n); 316 | for(int i=1;i<=n;i++) 317 | { 318 | scanf("%d%d",&a[i].x,&a[i].y); 319 | if(a[i].x>a[i].y) a[i].d=1; 320 | else if(a[i].x0`, 然后更新 `ans+=(Xi+1−Xi)*len`。 28 | 本题的数据均为浮点数,所以要离散化,具体做法见代码。 29 | 30 | ### 代码示例 31 | ```c++ 32 | #include 33 | #include 34 | #include 35 | #include 36 | using namespace std; 37 | #define Max 210 38 | int cov[Max<<2]; 39 | double len[Max<<2]; 40 | struct Edge{ 41 | double x, y1, y2; 42 | int k; 43 | }; 44 | Edge eg[Max<<1]; 45 | double y[Max<<1]; 46 | void pushup(int l, int r, int rt){ 47 | if(cov[rt] > 0)len[rt] = y[r+1] - y[l];//将一个点理解为这个点到其下个点之间的线段,所以r+1 48 | else if(l == r)len[rt] = 0; 49 | else len[rt] = len[rt<<1] + len[rt<<1|1]; 50 | } 51 | //更新ll-rr之间的值 52 | void update(int ll, int rr, int val, int l, int r, int rt){ 53 | if(ll <= l && r <= rr){ 54 | cov[rt] += val; 55 | pushup(l, r, rt); 56 | return; 57 | } 58 | int m = (l+r)>>1; 59 | if(ll<=m)update(ll, rr, val, l, m, rt<<1); 60 | if(rr>m)update(ll, rr, val, m+1 ,r, rt<<1|1); 61 | pushup(l, r, rt); 62 | } 63 | //将扫描线按x从小到大排序 64 | bool cmp(Edge a, Edge b){ 65 | return a.x < b.x; 66 | } 67 | int main(){ 68 | int n, cs = 0; 69 | while(scanf("%d", &n) && n!=0){ 70 | int k = 0; 71 | double x1, y1, x2, y2; 72 | for(int i=0; i 140 | #include 141 | #include 142 | #include 143 | using namespace std; 144 | #define Max 5010 145 | int len[Max<<2];//记录整个区间的有效长度 146 | int sum[Max<<2];//记录整个区间被整体覆盖了几次 147 | bool lt[Max<<2], rit[Max<<2];//记录该区间的左右端点是否被覆盖,便于合并区间 148 | int num[Max<<2];//记录整个区间被几条互不相交的线段覆盖 149 | struct Edge{ 150 | int x, y1, y2; 151 | int k; 152 | }; 153 | Edge eg[Max<<1]; 154 | int y[Max<<1]; 155 | void pushup(int l, int r, int rt){ 156 | if(sum[rt] > 0){ 157 | len[rt] = y[r+1] - y[l]; 158 | num[rt] = 1; 159 | lt[rt] = rit[rt] = 1; 160 | } 161 | else if(l == r){ 162 | len[rt] = 0; 163 | num[rt] = 0; 164 | lt[rt] = rit[rt] = 0; 165 | } 166 | else{ 167 | len[rt] = len[rt<<1] + len[rt<<1|1]; 168 | num[rt] = num[rt<<1] + num[rt<<1|1]; 169 | if(rit[rt<<1]==1 && lt[rt<<1|1]==1)num[rt] --; 170 | lt[rt] = lt[rt<<1]; 171 | rit[rt] = rit[rt<<1|1]; 172 | } 173 | } 174 | //更新ll-rr之间的值 175 | void update(int ll, int rr, int val, int l, int r, int rt){ 176 | if(ll <= l && r <= rr){ 177 | sum[rt] += val; 178 | pushup(l, r, rt); 179 | return; 180 | } 181 | int m = (l+r)>>1; 182 | if(ll<=m)update(ll, rr, val, l, m, rt<<1); 183 | if(rr>m)update(ll, rr, val, m+1 ,r, rt<<1|1); 184 | pushup(l, r, rt); 185 | } 186 | //将扫描线按x从小到大排序 187 | bool cmp(Edge a, Edge b){ 188 | return a.x < b.x; 189 | } 190 | int main(){ 191 | int n; 192 | while(scanf("%d", &n)!=EOF){ 193 | int k = 0; 194 | int x1, y1, x2, y2; 195 | for(int i=0; i 81 | #include 82 | 83 | typedef long long ll; 84 | #define pr(x) std::cout << #x << ":" << x << std::endl 85 | 86 | ll x,y,m,n,l; 87 | 88 | ll exgcd(ll a,ll b,ll &x,ll &y) { 89 | if(b == 0) { 90 | x = 1,y = 0; 91 | return a; 92 | } 93 | ll x2,y2; 94 | ll d = exgcd(b,a%b,x2,y2); 95 | x = y2; 96 | y = x2 - a/b*y2; 97 | return d; 98 | } 99 | int main() { 100 | std::cin >> x >> y >> m >> n >> l; 101 | ll k,t,d,a = n-m,b = l,s = x-y; 102 | if(a < 0) a *= -1,s *= -1; 103 | d = exgcd(a,b,k,t); 104 | ll r = b / d; 105 | if(s % d != 0) return 0 * puts("Impossible"); 106 | std::cout << ((k*(s/d))%r + r) % r << std::endl; 107 | return 0; 108 | } 109 | 110 | ``` 111 | 112 | 113 | ## 第二题: 裴蜀定理 114 | 115 | 题目链接[luogu4549](https://www.luogu.org/problemnew/show/P4549) 116 | 117 | ### 题目总结 118 | 非常简单,只有一句话,这里就不总结了. 119 | 120 | ### 解题思路 121 | 根据裴蜀定理的推广$A_1x_1+A_2x_2+...+A_nx_n = S$,有解的充分必要条件是: 122 | 123 | $gcd(A_1,A_2,...,A_n)|S$,因此$S$必须是$gcd(A_1,A_2,...,A_n)$的倍数,也就是说$S$作为最小的正数时只可能是$gcd(A_1,A_2,...,A_n)$. 124 | 125 | ### 代码 126 | ```cpp 127 | #include 128 | 129 | using namespace std; 130 | 131 | int gcd(int a,int b) { 132 | return !b?a:gcd(b,a%b); 133 | } 134 | 135 | int main() { 136 | int n; 137 | cin >> n; 138 | int ans = 0; 139 | for(int i = 1;i <= n;++i) { 140 | int a; 141 | cin >> a; 142 | if(a < 0) a = -a; 143 | ans = gcd(ans,a); 144 | } 145 | cout << ans << endl; 146 | } 147 | ``` 148 | 149 | ## 第三题: 同余方程 150 | 151 | 题目链接[luogu1082](https://www.luogu.org/problemnew/show/P1082) 152 | 153 | ### 题目总结 154 | 155 | 求关于$x$的同余方程 $a x \equiv 1(\bmod b)$ 的最小正整数解。 156 | 157 | ### 解题思路 158 | 159 | 这是一道很裸的题,本质上就是求逆元,但是我们要学会将同余方程转换成不定方程来进行解题的思路. 160 | 161 | $ax \equiv 1$可以转化成: 162 | 163 | 求不定方程$ax+by=1$的最小正整数解$x$. 164 | 165 | 依据我们解决不定方程问题的思路,先判断满足条件$gcd(a,b)|1$,即$gcd(a,b) = 1$ 166 | 167 | 随后使用扩展欧几里得求解方程求出一个特解$x_0$,为了将$x_0$转换成最小的正整数. 168 | 169 | 根据$x$的通解方程$x^* = x_0 + k*(b/1)$,需要将求得的$x_0$做如下处理: 170 | 171 | $(x_0\%b+b)\%b$ 172 | 173 | ### 代码 174 | ```cpp 175 | #include 176 | 177 | using namespace std; 178 | typedef long long ll; 179 | ll exgcd(ll a,ll b,ll &x,ll &y) { 180 | if(b == 0) { 181 | x = 1,y = 0; 182 | return a; 183 | } 184 | ll x2,y2; 185 | ll d = exgcd(b,a%b,x2,y2); 186 | x = y2; 187 | y = x2 - a/b*y2; 188 | return d; 189 | } 190 | int main() { 191 | ll a,b,x,y; 192 | cin >> a >> b; 193 | exgcd(a,b,x,y); 194 | cout << (x%b+b) % b << endl; 195 | } 196 | ``` 197 | ## 第四题: 禁止动规(较难,涉及到莫比乌斯反演相关知识) 198 | 199 | 题目链接[禁止动规](https://ac.nowcoder.com/acm/contest/211/D?&headNav=acm) 200 | 201 | ### 题目总结 202 | 203 | 给出一个无限长的数轴,以及$n$个变量$x_1,x_2,...,x_n$.每个变量的值在$[1,m]$之间,且是整数. 204 | 205 | 你一开始位于原点,每次只能向左或是向右跳$x_i$个单位,问走到原点右侧一个单位距离的概率有多大? 206 | 207 | 既随机给定n个变量后,存在至少一种从数轴上的0点走到1点的方案的概率 208 | 209 | 设答案为$ω$,那么你只需要输出$w*m^n$在模&2^64&意义下的值. 210 | 211 | ### 题目解答 212 | 213 | 根据裴蜀定理,当存在一些x的组合,使得它们的gcd为1的时候,$p_1x_1 + p_2x_2 + ... + p_nx_n = 1$,方程一定有解. 214 | 215 | 因此我们就求$gcd(x_1,x_2,...,x_n) = 1$的方案数即可 216 | 217 | 记$f(x)$表示$gcd(x_1,x_2,...,x_n) = x$的方案数 218 | 219 | 然后莫比乌斯反演 220 | 221 | $(\lfloor \frac{m}{x} \rfloor )^n = \sum_{x|d}{f(d)}$ 222 | 223 | 得到 224 | 225 | $f(x) = \sum_{x|d}{\mu(\frac{d}{x})*(\lfloor \frac{m}{d} \rfloor )^n}$ 226 | 227 | 答案就是 228 | 229 | $f(1) = \sum_{d=1}^{m}\mu(d)*(\lfloor \frac{m}{d} \rfloor )^n$ 230 | 231 | 对$(\lfloor \frac{m}{d} \rfloor )$分块,然后杜教筛求莫比乌斯函数前缀和就可以了. 232 | 233 | 234 | ### 代码 235 | ```cpp 236 | #include 237 | #include 238 | #include 239 | #include 240 | #include 241 | #define pr(x) std::cout << #x << ':' << x << std::endl 242 | #define rep(i,a,b) for(int i = a;i <= b;++i) 243 | #define clr(x) memset(x,0,sizeof(x)) 244 | #define setinf(x) memset(x,0x3f,sizeof(x)) 245 | 246 | 247 | typedef long long ll; 248 | typedef unsigned long long ull; 249 | const int N = 10000000; 250 | ull mu[N+10];int prime[N+10],pcnt,zhi[N+10]; 251 | void sieve(){ 252 | pcnt = 0; 253 | mu[1] = zhi[1] = 1; 254 | for(int i = 2;i <= N;++i){ 255 | if(!zhi[i]) mu[i] = -1,prime[pcnt++] = i; 256 | for(int j = 0;j < pcnt && prime[j]*i <= N;++j){ 257 | zhi[i*prime[j]] = 1; 258 | if(i % prime[j] == 0){ 259 | mu[i*prime[j]] = 0; 260 | break; 261 | } 262 | else{ 263 | mu[i*prime[j]] = -mu[i]; 264 | } 265 | } 266 | } 267 | for(int i = 1;i <= N;++i) mu[i] += mu[i-1]; 268 | } 269 | std::unordered_map rec,vis; 270 | ull Mu(ll x){ 271 | if(x <= N) return mu[x]; 272 | if(vis[x]) return rec[x]; 273 | ull res = 1,now = x,nxt; 274 | while(now >= 2){ 275 | nxt = x/(x/now+1); 276 | res -= (now - nxt) * Mu(x/now); 277 | now = nxt; 278 | } 279 | vis[x] = 1; 280 | return rec[x] = res; 281 | } 282 | ll n,m; 283 | ull mod_pow(ull x,ll n) { 284 | ull res = 1; 285 | while(n) { 286 | if(n & 1) 287 | res *= x; 288 | x *= x; 289 | n >>= 1; 290 | } 291 | return res; 292 | } 293 | int main() { 294 | sieve(); 295 | unsigned long long ans = 0; 296 | std::cin >> n >> m; 297 | ll nxt; 298 | for(ll x = m;x >= 1;x = nxt) { 299 | nxt = m/(m/x+1); 300 | ans += mod_pow(m/x,n) * (ull)(Mu(x) - Mu(nxt)); 301 | } 302 | std::cout << ans << std::endl; 303 | return 0; 304 | } 305 | ``` 306 | 307 | -------------------------------------------------------------------------------- /08-string1/kmp专题练习.md: -------------------------------------------------------------------------------- 1 | # 字符串初阶 2 | 3 | 4 | 对于初阶字符串问题而言,我们最常用的莫过于KMP问题,因此本章会整理一些有关于KMP的题型,并且总结其中的规律与问题 5 | 6 | 7 | ## KMP字符串匹配 8 | 9 | [洛谷P3375](https://www.luogu.org/problemnew/show/P3375) 10 | 11 | ### 题目大意 12 | 13 | 给定两个字符串s1 和 s2 ,其中 s2 为s1的子串,求出s2 在s1中所有出现的位置,并输出子串的前缀数组next 14 | 15 | ### 题目分析 16 | 17 | 这道题就是KMP的一个经典的模板题型,通过kmp算法先求出next数组,然后对模式串和目标串进行匹配。最后确定出现位置即可 18 | 19 | ### 代码 20 | 21 | https://paste.ubuntu.com/p/DTg99Hk8Ry/ 22 | 23 | ```c++ 24 | #include 25 | #include 26 | #define MAXN 1000010 27 | using namespace std; 28 | int kmp[MAXN]; 29 | int la,lb,j; 30 | char a[MAXN],b[MAXN]; 31 | int main() 32 | { 33 | cin>>a+1; 34 | cin>>b+1; 35 | la=strlen(a+1); 36 | lb=strlen(b+1); 37 | for (int i=2;i<=lb;i++) 38 | { 39 | while(j&&b[i]!=b[j+1]) 40 | j=kmp[j]; 41 | if(b[j+1]==b[i])j++; 42 | kmp[i]=j; 43 | } 44 | j=0; 45 | for(int i=1;i<=la;i++) 46 | { 47 | while(j>0&&b[j+1]!=a[i]) 48 | j=kmp[j]; 49 | if (b[j+1]==a[i]) 50 | j++; 51 | if (j==lb) {cout< 78 | #include 79 | #include 80 | #include 81 | #include 82 | using namespace std; 83 | typedef long long LL; 84 | const int MAXN=1024*1024; 85 | int n,next[MAXN]; 86 | char str[MAXN]; 87 | int main(){ 88 | scanf("%d%s",&n,str+1); 89 | for(int i=2,j=0;i<=n;i++){ 90 | while(j&&str[j+1]!=str[i]){ 91 | j=next[j]; 92 | } 93 | if(str[j+1]==str[i]){ 94 | j++; 95 | } 96 | next[i]=j; 97 | } 98 | printf("%d",n-next[n]); 99 | return 0; 100 | } 101 | ``` 102 | 103 | ## uva 1328 104 | 105 | [uva 1328](https://www.luogu.org/problemnew/show/UVA1328) 106 | 107 | ### 题目大意 108 | 109 | 给定一个字符串,求该字符串的最小循环节 110 | 111 | ### 题目分析 112 | 113 | 这个题和上面那个题是同一种题目,主要考点还是在next数组的应用上面 114 | 115 | ### 代码 116 | 117 | https://paste.ubuntu.com/p/67qP8Njmv4/ 118 | 119 | ```c++ 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | #include 127 | #include 128 | #include 129 | using namespace std; 130 | 131 | #define maxn 1000000 132 | 133 | int n; 134 | char a[maxn+5]; 135 | int nxt[maxn+5]={0}; 136 | 137 | int main() { 138 | 139 | int T=0; 140 | while(~scanf("%d",&n)&&n!=0){ 141 | printf("Test case #%d\n",++T); 142 | scanf("%s",a); 143 | nxt[0]=-1; 144 | for(int i=0;i=0&&a[j]!=a[i]){ 147 | j=nxt[j]; 148 | } 149 | nxt[i+1]=++j; 150 | } 151 | for(int i=1;i<=n;i++){ 152 | if(nxt[i]&&i%(i-nxt[i])==0){ 153 | printf("%d %d\n",i,i/(i-nxt[i])); 154 | } 155 | } 156 | printf("\n"); 157 | } 158 | 159 | return 0; 160 | } 161 | ``` 162 | 163 | ## [POI2006]OKR-Periods of Words 164 | 165 | [洛谷P3435](https://www.luogu.org/problemnew/show/P3435) 166 | 167 | ### 题目大意 168 | 169 | 给定一个字符串,对于每一个前缀,求它的最长前缀,使其重复后能够覆盖原串 170 | 171 | ### 题目分析 172 | 173 | 这道题目也是基于next数组的一个分析的过程,我们要使某个前缀重复以后能够覆盖整个串,我们只要求出当前子串的前缀=后缀的最短的匹配长度,那么剩余长度就是我们要求的最长前缀。 174 | 那么怎么求最短的匹配长度呢? 175 | 176 | `next[i]` 表示当前匹配的最长前缀和后缀相等 177 | 我们只需要继续求 `next[next[i]]` 的话,也就是依然满足前缀=后缀的情况。只要一直求,求到`0`为止,就可以得出最短的匹配长度了 178 | 179 | ### 代码 180 | 181 | https://paste.ubuntu.com/p/6VsRgDj2QQ/ 182 | 183 | ```c++ 184 | #include 185 | #include 186 | #include 187 | #include 188 | #define ll long long 189 | using namespace std; 190 | char a[1000010];int n,fail[1000010]; 191 | int main(){ 192 | scanf("%d",&n);scanf("%s",a);int i,j;ll cnt=0; 193 | fail[0]=fail[1]=0;j=0; 194 | for(i=1;i 225 | #include 226 | #include 227 | #include 228 | #include 229 | 230 | using namespace std; 231 | #define mod 1000000007 232 | #define N 1000005 233 | int dp[N],t,next[N],n,num[N]; 234 | char s[N]; 235 | inline void KMP(long long ans=1) 236 | { 237 | n=strlen(s); 238 | next[0]=-1; 239 | int j; 240 | for (int i=0;ii+1) j=next[j]; 255 | ans*=num[j]+1;ans%=mod; 256 | } 257 | cout< 283 | using namespace std; 284 | const int maxn=1e6+50; 285 | const int inf=0x3f3f3f3f; 286 | const int mod =1e9+7; 287 | int next[maxn]; 288 | int _hash[maxn]; 289 | void getnext(string s) 290 | { 291 | int len=s.size(); 292 | int i=0,j=-1; 293 | next[0]=-1; 294 | while(i>a; 311 | getnext(a); 312 | int len = a.size(); 313 | int k=next[len]; 314 | 315 | while(k) 316 | { 317 | if(_hash[k]) 318 | { 319 | for(int i=0;i 26 | 27 | using namespace std; 28 | 29 | const int maxm = 30; 30 | const int maxn = 30000 + 10; 31 | 32 | int v[maxm],w[maxm]; 33 | int dp[maxn]; 34 | 35 | int main() 36 | { 37 | int n,m; 38 | scanf("%d%d",&n,&m); 39 | for(int i = 1; i <= m; i++) 40 | { 41 | scanf("%d%d",&v[i],&w[i]); 42 | w[i] *= v[i]; 43 | } 44 | for(int i = 1; i <= m; i++) 45 | for(int j = n; j-v[i] >= 0; j--) 46 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 47 | printf("%d\n",dp[n]); 48 | return 0; 49 | } 50 | 51 | ``` 52 | 53 | ## 小A点菜 54 | 55 | [luogu1164](https://www.luogu.org/problemnew/show/P1164) 56 | 57 | ### 题意 58 | 59 | uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种。 60 | uim指着墙上的价目表(太低级了没有菜单),说:“随便点”。 61 | 不过uim由于买了一些辅(e)辅(ro)书,口袋里只剩M元(M≤10000)。 62 | 餐馆虽低端,但是菜品种类不少,有 N 种 (N≤100),第i种卖 a[i] 63 | 元)(a[i] ≤ 1000)。 64 | 由于是很低端的餐馆,所以每种菜只有一份。 65 | 小A奉行“不把钱吃光不罢休”,所以他点单一定刚好吧uim身上所有钱花完。他想知道有多少种点菜方法。 66 | 由于小A肚子太饿,所以最多只能等待11秒。 67 | 68 | 输入: 69 | 第一行是两个数字,表示NN和MM。 70 | 第二行起NN个正数a[i], 可以有相同的数字,每个数字均在1000以内。 71 | 输出: 72 | 一个正整数,表示点菜方案数,保证答案的范围在int之内。 73 | 74 | ### 思路 75 | 76 | 01背包模板题目。第二种01背包问题类型,设dp[i][j], 为考虑前i件物品用光j元的方案数。 77 | (1)if(j==第i道菜的价格) dp[i][j] = dp[i-1][j]+1; 78 | (2)if(j>第i道菜的价格) dp[i][j] = dp[i-1][j]+dp[i-1][j-第i道菜的价格]; 79 | (3)if(j<第i道菜的价格) dp[i][j] = dp[i-1][j]; 80 | 81 | 也可用滚动数组优化,初始化时dp[] = {0}, dp[0] = 1; 82 | 动态转移方程为 dp[j] += dp[j-a[i]]; (j: m : a[i]) 83 | 84 | ### 代码 85 | 86 | ```c++ 87 | #include 88 | 89 | using namespace std; 90 | 91 | const int maxm = 10000 + 10; 92 | const int maxn = 100 + 10; 93 | 94 | int a[maxn]; 95 | int dp[maxm] = {0}; 96 | 97 | int main() 98 | { 99 | int n,m; 100 | dp[0] = 1; 101 | scanf("%d%d",&n,&m); 102 | for(int i = 1; i <= n; i++) 103 | { 104 | scanf("%d",&a[i]); 105 | for(int j = m; j >= a[i]; j--) 106 | dp[j] += dp[j-a[i]]; 107 | } 108 | printf("%d\n",dp[m]); 109 | return 0; 110 | } 111 | 112 | ``` 113 | 114 | 115 | ## 采药 116 | 117 | [luogu1048](https://www.luogu.org/problemnew/show/P1048) 118 | 119 | ### 题意 120 | 121 | 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 122 | 如果你是辰辰,你能完成这个任务吗? 123 | 124 | 输入: 125 | 第一行有2个整数 T (1 ≤ T ≤ 1000) 和 M ( 1 ≤ M ≤ 100),用一个空格隔开,T代表总共能够用来采药的时间,MM代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。 126 | 输出: 127 | 1个整数,表示在规定的时间内可以采到的草药的最大总价值。 128 | 129 | ### 思路 130 | 01背包模板题目,采药时间作为限制条件,等同于背包问题的体积,药品价值作为价值衡量,然后利用套用01背包模板解题。 131 | 132 | ### 代码 133 | 134 | ```c++ 135 | #include 136 | 137 | using namespace std; 138 | const int maxn = 100 + 10; 139 | const int maxc = 1000 + 10; 140 | 141 | int dp[maxc] = {0}; 142 | int v[maxn],w[maxn]; 143 | int main() 144 | { 145 | int T,M; 146 | scanf("%d%d",&T,&M); 147 | for(int i = 1; i <= M; i++) 148 | scanf("%d%d",&v[i],&w[i]); 149 | for(int i = 1; i <= M; i++) 150 | for(int j = T; j >= v[i]; j--) 151 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 152 | printf("%d\n",dp[T]); 153 | return 0; 154 | } 155 | 156 | ``` 157 | 158 | ## 疯狂的采药 159 | 160 | [luogu1616](https://www.luogu.org/problemnew/show/P1616) 161 | 162 | ### 题意 163 | 164 | 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。” 165 | 如果你是LiYuxiang,你能完成这个任务吗? 166 | 此题和原题的不同点: 167 | 1.每种草药可以无限制地疯狂采摘。 168 | 2.药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了! 169 | 如果你是辰辰,你能完成这个任务吗? 170 | 输入: 171 | 输入第一行有两个整数T(1 <= T <= 100000)和M(1 <= M <= 10000),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到10000之间(包括1和10000)的整数,分别表示采摘某种草药的时间和这种草药的价值。 172 | 输出: 173 | 1个整数,表示在规定的时间内可以采到的草药的最大总价值 174 | 175 | ### 思路 176 | 177 | 完全背包模板题目,采药时间作为限制条件,等同于背包问题的体积,药品价值作为价值衡量,然后利用套用完全背包模板解题。 178 | 179 | ### 代码 180 | 181 | ```c++ 182 | #include 183 | 184 | using namespace std; 185 | const int maxc = 100000 + 10; 186 | const int maxn = 10000 + 10; 187 | 188 | int dp[maxc] = {0}; 189 | int v[maxn],w[maxn]; 190 | int main() 191 | { 192 | int T,M; 193 | scanf("%d%d",&T,&M); 194 | for(int i = 1; i <= M; i++) 195 | scanf("%d%d",&v[i],&w[i]); 196 | for(int i = 1; i <= M; i++) 197 | for(int j = v[i]; j <= T; j++) 198 | dp[j] = max(dp[j],dp[j-v[i]] + w[i]); 199 | printf("%d\n",dp[T]); 200 | return 0; 201 | } 202 | ``` 203 | 204 | ## 装箱问题 205 | 206 | [luogu1049](https://www.luogu.org/problemnew/show/P1049) 207 | 208 | ### 题意 209 | 210 | 有一个箱子容量为V(正整数,0 ≤ V ≤ 20000),同时有n个物品 `0 226 | 227 | using namespace std; 228 | const int maxc = 20000 + 10; 229 | const int maxn = 30 + 10; 230 | 231 | int v[maxn]; 232 | int dp[maxc] = {0}; 233 | 234 | int main() 235 | { 236 | int V,n; 237 | scanf("%d%d",&V,&n); 238 | for(int i = 1; i <= n; i++) scanf("%d",&v[i]); 239 | for(int i = 1; i <= n; i++) 240 | for(int j = V; j >= v[i]; j--) 241 | dp[j] = max(dp[j],dp[j-v[i]] + v[i]); 242 | printf("%d\n",V-dp[V]); 243 | return 0; 244 | } 245 | 246 | ``` 247 | 248 | ## HDU3466 Proud Merchants 249 | 250 | 测试网站[HDU3466](http://acm.hdu.edu.cn/showproblem.php?pid=3466) 251 | 252 | ### 题目描述 253 | 254 | 现在`n`个物品,每组物品有三个属性,`pi`,买这种物品你需要花费的钱,`vi`, 该物品的价值。qi,如果你想要买这种物品你所拥有的钱必须大于`qi`。 255 | 256 | 问你能用你所有的钱最多能获得多大价值。(每个物品只能买一次) 257 | 258 | ## 题目分析 259 | 260 | 因为每个物品都有一个限制q,而01背包dp是从前i个物品转移到前i+1个物品,所以必须保证转移时无后效性,即前面i个物品选择的结果不会影响到后面的选择. 261 | 在这里体现在前i个物品中会影响的范围为`q-p`;如果第`i+1`个物品的`q-p > 前i个物品的q-p`,则可以保证如果选择第`i`个物品,那么前面`i+1`个物品的`q`的限制条件都能满足。 即 `q[i]+1` 在的位置>`q[i]` 262 | 263 | 264 | ## 代码示例 265 | 266 | ```c++ 267 | #include 268 | 269 | using namespace std; 270 | typedef long long LL; 271 | const int maxn = 510; 272 | const int INF = 0x3f3f3f3f; 273 | 274 | int n,m; 275 | int dp[5005]; 276 | struct Node{ 277 | int p,q,v; 278 | }a[maxn]; 279 | 280 | bool cmp (Node a, Node b){ 281 | return a.q - a.p < b.q - b.p; 282 | } 283 | 284 | int main() 285 | { 286 | while(~scanf("%d%d",&n,&m)){ 287 | for(int i = 0; i < n; i++) scanf("%d%d%d",&a[i].p,&a[i].q,&a[i].v); 288 | sort(a,a+n,cmp);//排序 289 | memset(dp,0,sizeof(dp));//初始化dp数组 290 | for(int i = 0; i < n; i++) 291 | for(int j = m; j >= a[i].q; j--) 292 | dp[j] = max(dp[j],dp[j-a[i].p] + a[i].v);//无后效性dp 293 | printf("%d\n",dp[m]); 294 | } 295 | return 0; 296 | } 297 | ``` 298 | 299 | -------------------------------------------------------------------------------- /16-binary/二分与模拟.md: -------------------------------------------------------------------------------- 1 | # 二分与模拟 2 | 3 | 测试网站[洛谷1083 借教室](https://www.luogu.org/problemnew/show/P1083) 4 | 5 | ## P1083 借教室 6 | 7 | ### 题目描述 8 | 9 | 在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 10 | 11 | 面对海量租借教室的信息,我们自然希望编程解决这个问题。 12 | 13 | 我们需要处理接下来`n`天的借教室信息,其中第`i`天学校有`r[i]`个教室可供租借。共有`m` 份订单,每份订单用三个正整数描述,分别为`d[j]`, `s[j]`, `t[j\]`,表示某租借者需要从第 `s[j]` 天到第 `t[j]` 天租借教室(包括第`s[j]`天和第`t[j]`天),每天需要租借`d[j]`个教室。 14 | 15 | 我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供`d[j]`个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。 16 | 17 | 借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 `s[j]` 天到第 `t[j]` 天中有至少一天剩余的教室数量不足 `d[j]` 个。 18 | 19 | 现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。 20 | 21 | ### 输入格式 22 | 23 | 第一行包含两个正整数`n`,`m`,表示天数和订单的数量。 24 | 25 | 第二行包含`n`个正整数,其中第`i`个数为`r[i]`,表示第`i`天可用于租借的教室数量。 26 | 27 | 接下来有`m`行,每行包含三个正整数`d[j]`, `s[j]`,`t[j]`, 表示租借的数量,租借开始、结束分别在第几天。 28 | 29 | 每行相邻的两个数之间均用一个空格隔开。天数与订单均用从`1`开始的整数编号。 30 | 31 | ### 输出格式 32 | 33 | 如果所有订单均可满足,则输出只有一行,包含一个整数`0`。否则(订单无法完全满足) 34 | 输出两行,第一行输出一个负整数`-1`,第二行输出需要修改订单的申请人编号。 35 | 36 | ### 输入样例 37 | 38 | 4 3 39 | 2 5 4 3 40 | 2 1 3 41 | 3 2 4 42 | 4 2 4 43 | 44 | ### 输出样例 45 | 46 | -1 47 | 2 48 | 49 | ### 数据范围 50 | 51 | - 对于10%的数据,有`1 ≤ n, m ≤ 10`; 52 | - 对于30%的数据,有`1 ≤ n, m ≤ 1000`; 53 | - 对于70%的数据,有`1 ≤ n, m ≤ 10^5`; 54 | - 对于100%的数据,有`1 ≤ n, m ≤ 10^6, 0 ≤ r[i], d[j] ≤ 10^9, 1 ≤ s[j] ≤ t[j] ≤n`. 55 | 56 | 57 | ### 题目分析 58 | 59 | 首先我们能够想到的做法肯定是暴力,我们可以暴力比较当前订单所需求的某一天教室数量和该天实际剩余数量。 60 | 61 | 代码如下: 62 | 63 | ```c++ 64 | #include 65 | #include 66 | #include 67 | #include 68 | using namespace std; 69 | #define mst(a,b) memset((a),(b),sizeof(a)) 70 | #define f(i,a,b) for(int i=(a);i<=(b);++i) 71 | #define rush() int T;scanf("%d",&T);while(T--) 72 | 73 | typedef long long ll; 74 | const int maxn= 1000005; 75 | const ll mod = 1e9+7; 76 | const int INF = 0x3f3f3f3f; 77 | const double eps = 1e-6; 78 | 79 | int n,m; 80 | int a[maxn]; 81 | 82 | int main() 83 | { 84 | scanf("%d%d",&n,&m); 85 | for(int i=1;i<=n;i++) scanf("%d",&a[i]); 86 | for(int i=1;i<=m;i++) 87 | { 88 | int x,l,r; 89 | scanf("%d%d%d",&x,&l,&r); 90 | for(int j=l;j<=r;j++) 91 | { 92 | a[j]-=x; 93 | if(a[j]<0) return printf("-1\n%d\n",i),0; 94 | } 95 | } 96 | puts("0"); 97 | } 98 | 99 | ``` 100 | 101 | 提交至测试网站,可以发现有部分数据超时,只得到了45分。 102 | 103 | ![image](images/1083.png) 104 | 105 | 经过分析,这样的暴力做法时间复杂度为`O(n * m)`,显然会超时。 106 | 107 | 于是我们考虑问题存在的性质,易得:如果第`j`个订单可满足的话,第`i`个订单一定可满足。`(i 115 | #include 116 | #include 117 | #include 118 | using namespace std; 119 | #define mst(a,b) memset((a),(b),sizeof(a)) 120 | #define rush() int T;scanf("%d",&T);while(T--) 121 | 122 | typedef long long ll; 123 | const int maxn= 1000005; 124 | const ll mod = 1e9+7; 125 | const int INF = 0x3f3f3f3f; 126 | const double eps = 1e-6; 127 | 128 | int n,m; 129 | ll a[maxn],x[maxn]; 130 | int s[maxn],t[maxn]; 131 | ll sum[maxn]; 132 | 133 | bool check(int pos) 134 | { 135 | mst(sum,0); 136 | for(int i=1;i<=pos;i++) 137 | { 138 | sum[s[i]]+=x[i]; 139 | sum[t[i]+1]-=x[i]; 140 | } 141 | ll now=0; 142 | for(int i=1;i<=n;i++) 143 | { 144 | now+=sum[i]; 145 | if(now>a[i]) return 1; //需求数大于实际数量 146 | } 147 | return 0; 148 | } 149 | 150 | int main() 151 | { 152 | scanf("%d%d",&n,&m); 153 | for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 154 | for(int i=1;i<=m;i++) 155 | { 156 | scanf("%lld%d%d",&x[i],&s[i],&t[i]); 157 | } 158 | int ans=-1; 159 | int l=1,r=m; 160 | while(l<=r) 161 | { 162 | int mid=(l+r)/2; 163 | if(check(mid)) //前mid个订单不能完全满足,则答案应该更小 164 | { 165 | ans=mid; 166 | r=mid-1; 167 | } 168 | else l=mid+1; 169 | } 170 | if(ans==-1) puts("0"); 171 | else printf("-1\n%d\n",ans); 172 | } 173 | 174 | ``` 175 | 176 | 177 | ## 聪明的质监员 178 | 179 | [luogu 1314](https://www.luogu.org/problemnew/show/P1314) 180 | 181 | ### 题意 182 | 183 | RT 184 | 185 | ### 思路 186 | 187 | 容易发现,`W`越大,矿产的检验结果`Y`越大,那么我们就可以根据这个性质进行二分。 188 | 189 | 当然,对于`Y`值的计算,需要用前缀和优化。 190 | 191 | 192 | ### 代码 193 | 194 | ```c++ 195 | #include 196 | using namespace std; 197 | #define mst(a,b) memset((a),(b),sizeof(a)) 198 | #define rush() int T;scanf("%d",&T);while(T--) 199 | 200 | typedef long long ll; 201 | const int maxn = 200005; 202 | const ll INF = 1e18; 203 | const ll mod=1e9+7; 204 | const double eps = 1e-9; 205 | 206 | int n,m; 207 | ll S; 208 | int w[maxn],num[maxn]; 209 | int l[maxn],r[maxn]; 210 | ll val[maxn],sum[maxn]; 211 | 212 | ll cal(int x) 213 | { 214 | ll tmp=0; 215 | for(int i=1;i<=n;i++) 216 | { 217 | sum[i]=sum[i-1]; 218 | num[i]=num[i-1]; 219 | if(w[i]>=x) 220 | { 221 | sum[i]+=val[i]; 222 | num[i]++; 223 | } 224 | } 225 | for(int i=0;i 286 | #include 287 | #include 288 | #include 289 | #include 290 | using namespace std; 291 | #define mst(a,b) memset((a),(b),sizeof(a)) 292 | #define rush() int T;scanf("%d",&T);while(T--) 293 | 294 | typedef long long ll; 295 | const int maxn = 200005; 296 | const ll INF = 1e18; 297 | const ll mod=1e9+7; 298 | const double eps = 1e-9; 299 | 300 | int n,m,d; 301 | int a[maxn]; 302 | sets; 303 | mapmp; 304 | int ans[maxn]; 305 | 306 | int main() 307 | { 308 | scanf("%d%d%d",&n,&m,&d); 309 | for(int i=0;i::iterator it=s.lower_bound(tmp+d+1); 328 | if(it==s.end()) break; 329 | tmp=*it; 330 | ans[mp[tmp]]=now; 331 | pos=tmp; 332 | s.erase(tmp); 333 | } 334 | } 335 | printf("%d\n",now); 336 | for(int i=0;i 首先来一份模板 4 | 5 | ```c++ 6 | //string1 = "aa" -> "$#a#a#" 7 | //string2 = "aba" -> "$#a#b#a#" 8 | char s[N]; 9 | char ss[2*N]; 10 | int p[2*N]; 11 | void manacher(char s[],int len){ 12 | ss[0]='$'; 13 | for(int i=1;i<=2*len+1;i+=2)ss[i]='#'; 14 | for(int i=0;imx?1:min(mx-i,p[2*id-i]); 20 | while(ss[i+p[i]]==ss[i-p[i]])p[i]++; 21 | MaxLen=max(MaxLen,p[i]-1);//回文长度==p[i]-1 22 | if(i+p[i]>mx)mx=i+p[i],id=i; 23 | } 24 | } 25 | ``` 26 | 27 | ## Non Super Boring Substring 28 | 29 | [Codeforces Gym-101864J]() 30 | 31 | ### 题意 32 | 33 | 给出一个回文串,问有多少个**子串中不包含长度至少k的回文串**的子串 34 | 35 | ### 思路 36 | 37 | 首先当然是直接套马拉车板子,得出回文串长度 38 | 39 | 如果我们可以快速处理出以i点作为右端点的串有多少个包含k长回文串,题目就做出来了 40 | 41 | ### 代码 42 | 43 | ```C++ 44 | #include 45 | using namespace std; 46 | #define debug(i) printf("# %d\n",i) 47 | typedef long long LL; 48 | const int N=1e5+5; 49 | char s[N]; 50 | char ss[2*N]; 51 | int p[2*N]; 52 | void manacher(char s[],int len){ 53 | ss[0]='$';for(int i=1;i<=2*len+1;i+=2)ss[i]='#'; 54 | for(int i=0;imx?1:min(mx-i,p[2*id-i]); 59 | while(ss[i+p[i]]==ss[i-p[i]])p[i]++; 60 | MaxLen=max(MaxLen,p[i]-1); 61 | if(i+p[i]>mx)mx=i+p[i],id=i; 62 | } 63 | } 64 | 65 | int tr[N]; 66 | void add(int pos,int v,int n){ 67 | while(pos<=n)tr[pos]=max(tr[pos],v),pos+=(pos&(-pos)); 68 | } 69 | int query(int pos){ 70 | int re=0; 71 | while(pos>=1)re=max(tr[pos],re),pos-=(pos&(-pos));return re; 72 | } 73 | 74 | int main(){ 75 | int t;scanf("%d",&t); 76 | while(t--){ 77 | memset(tr,0,sizeof(tr)); 78 | int k;scanf("%d",&k); 79 | scanf("%s",s); 80 | int len=strlen(s); 81 | int Len=2*len+1; 82 | manacher(s,len); 83 | 84 | for(int i=2;i) 106 | 107 | ### 题意 108 | 109 | 求一个最长回文串,回文串的前一半满足非严格单调递增,后一半满足非严格单调递减 110 | 111 | ### 思路 112 | 113 | 模板魔改题目,改一改马拉车模板,怼上 114 | 115 | ### 代码 116 | 117 | ```C++ 118 | #include 119 | #include 120 | #include 121 | #include 122 | using namespace std; 123 | const int maxn=100005; 124 | int str[maxn]; 125 | int tmp[maxn<<1]; 126 | int Len[maxn<<1]; 127 | 128 | int N; 129 | int INIT(int *st){ 130 | int i,len=N; 131 | tmp[0]=-2; 132 | tmp[1]=-1; 133 | for(i=0;ii) 150 | Len[i]=min(Len[2*pos-i],Len[pos]+pos-i); 151 | else 152 | Len[i]=1; 153 | while(st[i-Len[i]]==st[i+Len[i]]&&label){ 154 | if(st[i-Len[i]]<0) 155 | Len[i]++; 156 | else if(st[i-Len[i]]<=st[i+Len[i]-2]) 157 | Len[i]++; 158 | else 159 | label=false; 160 | } 161 | if(Len[i]+i>pos+Len[pos]) 162 | pos=i; 163 | ans=max(ans,Len[i]); 164 | } 165 | return ans-1; 166 | } 167 | int main(){ 168 | int T; 169 | scanf("%d",&T); 170 | while(T--){ 171 | scanf("%d",&N); 172 | for(int i=0;i 原题失踪,给出描述 186 | > 187 | > ``` 188 | > Description 189 | > 母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字 190 | > 母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 191 | > 192 | > Input 193 | > 输入数据有多行,每行一个字符串,表示目标项链的样式。 194 | > 195 | > Output 196 | > 多行,每行一个答案表示最少需要使用第二个机器的次数。 197 | > 198 | > Sample Input 199 | > abcdcba 200 | > abacada 201 | > abcdef 202 | > 203 | > Sample Output 204 | > 0 205 | > 2 206 | > 5 207 | > 208 | > 209 | > HINT 210 | > 每个测试数据,输入不超过 5行 211 | > 每行的字符串长度小于等于 50000 212 | > ``` 213 | 214 | ### 题意 215 | 216 | 给出操作,链接回文串生成特殊字符串,输出次数 217 | 218 | ### 思路 219 | 220 | 马拉车初始化,问题转化为区间覆盖问题 221 | 222 | ### 代码 223 | 224 | ```C++ 225 | #include 226 | #include 227 | #include 228 | using namespace std; 229 | 230 | const int N=50000+5; 231 | 232 | int len,pal[N*2]; 233 | char s[N],a[N*2]; 234 | struct Node{ 235 | int le,ri; 236 | }qu[N*2]; 237 | 238 | bool cmp(Node a,Node b){ 239 | return a.le=i) pal[i]=min(mx-i+1,pal[2*id-i]); 257 | else pal[i]=1; 258 | while(a[i-pal[i]]==a[i+pal[i]]) ++pal[i]; 259 | if(mxqu[far].ri) far=i; 268 | while(i<=len){ 269 | ans++; 270 | int tmp=far; 271 | for(;qu[i].le<=qu[far].ri;i++) 272 | if(qu[i].ri>qu[tmp].ri) tmp=i; 273 | far=tmp; 274 | } 275 | return ans; 276 | } 277 | int main(){ 278 | while(scanf("%s",s)!=EOF){ 279 | len=strlen(s); 280 | insert(); 281 | manacher(); 282 | memset(qu,0,sizeof(qu)); 283 | for(int i=1;i<=len;i++) 284 | qu[i].le=i-pal[i]+1,qu[i].ri=i+pal[i]-1; 285 | sort(qu+1,qu+len+1,cmp); 286 | printf("%d\n",fugai()-1); 287 | } 288 | return 0; 289 | } 290 | ``` 291 | 292 | ## Palindrome 293 | 294 | [POJ-1159]() 295 | 296 | ### 题意 297 | 298 | 给定一个字符串L,为了保证L是回文序列需要最少添加几个字符; 299 | 300 | ### 思路 301 | 302 | 做法是得到L的逆序列S,求L和S的最长公共子序列,用到动态规划的知识,如果运用$maxlen[i][j]=max(maxlen[i-1][j], maxlen[i][j-1])$会爆内存,所以用滚动数组来实现,因为当我们用完maxlen[i][j]后就用一次,所以可以把5000\*5000的数组缩小到2 \* 5000 303 | 304 | ### 代码 305 | 306 | ```C++ 307 | #include 308 | #include 309 | #include 310 | using namespace std; 311 | char L[5050], S[5050]; 312 | int maxl[2][5050]; 313 | int main(){ 314 | int n,i,j; 315 | while(cin>>n){ 316 | for(i=1; i<=n; i++) 317 | cin>>L[i]; 318 | L[n+1]='\0'; 319 | for(i=1; i<=n; i++) 320 | S[i]=L[n-i+1]; 321 | memset(maxl,0,sizeof(maxl)); 322 | int e=0; 323 | for(i=1; i<=n; i++){ 324 | e=1-e; 325 | for(j=0; j<=n; j++){ 326 | if(L[i]==S[j]) 327 | maxl[e][j]=maxl[1-e][j-1]+1; 328 | else 329 | maxl[e][j]=maxl[e][j-1]>maxl[1-e][j]?maxl[e][j-1]:maxl[1-e][j]; 330 | } 331 | } 332 | cout< 26 | using namespace std; 27 | 28 | struct Node{ 29 | int d, s; 30 | string ans; 31 | }; 32 | int d, s; 33 | bool vis[505][5005]; 34 | 35 | string bfs(){ 36 | queue Q; 37 | vis[0][0] = true; 38 | for (Q.push(Node{0, 0, ""}); !Q.empty();){ 39 | Node cur = Q.front(); Q.pop(); 40 | if (cur.d == 0 && cur.s == s) return cur.ans; 41 | for (int i = 0; i < 10; i++){ 42 | int dd = (cur.d * 10 + i) % d; 43 | int ss = cur.s + i; 44 | if (ss <= s && !vis[dd][ss]){ 45 | vis[dd][ss] = true; 46 | Q.push(Node{dd, ss, cur.ans + (char)(i+'0')}); 47 | } 48 | } 49 | } 50 | return "-1"; 51 | } 52 | 53 | int main(int argc, char** argv) { 54 | scanf("%d%d", &d, &s); 55 | cout << bfs() << endl; 56 | return 0; 57 | } 58 | ``` 59 | 60 | ![](images/whzNB.png) 61 | 62 | ## hdu1241 油田 63 | 64 | 测试网站[HDU1241](https://vjudge.net/problem/HDU-1241) 65 | 66 | ### 题目翻译 67 | 68 | GeoSurvComp地质调查公司负责探测地下石油储量。GeoSurvComp公司在一大块矩形区域内工作,创造出一个网格把土地分成很多方块。如果每一方块单独分析,使用感应设备去测定方块中是否含有石油。含有油的方块称为口袋。如果两个口袋是相邻的(上下左右或对角相邻),那它们属于同一个油床。油床可以相当大,可以包含众多口袋。你的任务就是测定出有多少不同的油床。 69 | 70 | ### 题目分析 71 | 72 | 这道题是典型的dfs。遍历所有点,如果是油田则将其联通的部分做个标记,表示已经搜过,记录一共搜过多少联通块即可。这里我的标记是直接将油田变成非油田。具体实现见代码。 73 | 74 | ### 代码示例 75 | ```c++ 76 | #include 77 | using namespace std; 78 | #define Max 110 79 | int n, m; 80 | string s[Max]; 81 | int dx[]={-1, -1, -1, 0, 0, 1, 1, 1}, dy[]={-1, 0, 1, -1, 1, -1, 0, 1};//定义搜索的八个方向 82 | void dfs(int i, int j){ 83 | s[i][j] = '*';//将已经搜过的@变成* 84 | for(int k=0; k<8; k++){ 85 | int nx = i+dx[k], ny = j+dy[k]; 86 | if(nx>=0 && nx=0 && ny> n >> m && n && m){ 94 | for(int i=0; i> s[i]; 96 | } 97 | int ans = 0; 98 | for(int i=0; iB1 121 | A2->B2 122 | 规则的含义为:在 A中的子串 A1可以变换为 B1,A2可以变换为B2…。 123 | 例如:A='abcd',B='xyz' 124 | 变换规则为: 125 | ‘abc’->‘xu’ ; ‘ud’->‘y’ ; ‘y’->‘yz’ ; 126 | 则此时,A可以经过一系列的变换变为B,其变换的过程为: 127 | ‘abcd’->‘xud’->‘xy’->‘xyz’ 128 | 共进行了3次变换,使得A变换为B。 129 | 130 | ### 输入样例 131 | 132 | ``` 133 | abcd xyz 134 | abc xu 135 | ud y 136 | y yz 137 | ``` 138 | 139 | ### 输出样例 140 | 141 | ``` 142 | 3 143 | ``` 144 | 145 | ### 题目分析 146 | 147 | 这道题给出初始状态,结尾状态和变换规则,求最小变换步数。很容易想到用bfs来解决。这里为了提高效率我用的双向bfs,即从起始状态和终止状态同时搜,直到两者相遇。 148 | 双向bfs需要开两个队列,一个从前往后入队搜索,一个从后往前入队搜索;再用两个map记录已搜过的状态,一是用来判该状态是否已访问过,二是存储该状态的变换步数。 149 | 然后是字符串变换,可以用string的substr和replace,具体用法见代码实现。 150 | 151 | ### 代码示例 152 | 153 | ```c++ 154 | #include 155 | using namespace std; 156 | #define Max 10 157 | struct Node{ 158 | string s; 159 | int deep; 160 | }; 161 | string a, b; 162 | string A[Max], B[Max]; 163 | int n=0; 164 | map map1, map2;//分别记录从前往后搜和从后往前搜的状态,map的值为当前状态变换的步数 165 | queue q1, q2;//两个队列,一个从初始状态开始搜,一个从最终状态搜 166 | int ans = 0; 167 | string Change1(string s, int i, int j){//正着转换字符串 168 | int l = A[j].length(); 169 | if(i+l > s.length())return ""; 170 | string s1 = s.substr(i, l); 171 | if(s1 == A[j]){ 172 | s.replace(i, l, B[j]); 173 | return s; 174 | } 175 | else return ""; 176 | } 177 | string Change2(string s, int i, int j){//倒着转换字符串 178 | int l = B[j].length(); 179 | if(i+l > s.length())return ""; 180 | string s1 = s.substr(i, l); 181 | if(s1 == B[j]){ 182 | s.replace(i, l, A[j]); 183 | return s; 184 | } 185 | else return ""; 186 | } 187 | void bfs(){ 188 | map1.clear(), map1[a] = 0;//将初始状态存下来 189 | map2.clear(), map2[b] = 0;//将结尾状态存下来 190 | Node now; 191 | now.s = a; 192 | now.deep = 0; 193 | q1.push(now); 194 | while(!q1.empty() || !q2.empty()){ 195 | if(!q1.empty()){ 196 | now = q1.front(); 197 | q1.pop(); 198 | if(now.deep > 6){ 199 | return ; 200 | } 201 | for(int i=0; i 6){ 226 | return ; 227 | } 228 | for(int i=0; i> a >> b; 251 | while(cin >> A[n] >> B[n]){ 252 | n ++; 253 | } 254 | if(a == b){//特判a=b的情况 255 | cout << 0 << endl; 256 | return 0; 257 | } 258 | bfs(); 259 | if(ans > 10 || ans == 0)cout << "NO ANSWER!\n"; 260 | else cout << ans << endl; 261 | return 0; 262 | } 263 | ``` 264 | 265 | 266 | ## 数独 267 | 268 | [luogu1784](https://www.luogu.org/problemnew/show/P1784) 269 | 270 | ```c++ 271 | #include 272 | using namespace std; 273 | const int N = 11; 274 | const int id[N][N] ={ 275 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 276 | {0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; 277 | {0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; 278 | {0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; 279 | {0, 4, 4, 4, 5, 5, 5, 6, 6, 6}; 280 | {0, 4, 4, 4, 5, 5, 5, 6, 6, 6}; 281 | {0, 4, 4, 4, 5, 5, 5, 6, 6, 6}; 282 | {0, 7, 7, 7, 8, 8, 8, 9, 9, 9}; 283 | {0, 7, 7, 7, 8, 8, 8, 9, 9, 9}; 284 | {0, 7, 7, 7, 8, 8, 8, 9, 9, 9}; 285 | } 286 | 287 | struct Blank{ 288 | int x, y; 289 | } arr[N*N]; 290 | int A; 291 | 292 | int row[N][N], col[N][N], area[N][N]; 293 | int g[N][N]; 294 | 295 | void putNum(int x, int y, int k){ 296 | g[x][y] = k; 297 | row[x][k] = col[y][k] = area[id[x][y]][k] = true; 298 | } 299 | 300 | void dfs(int dep){ 301 | if (dep == A+1){ 302 | print(); // ans = max(ans, get_score()); 303 | return; 304 | } 305 | 306 | for (int num = 1; num <= 9; num++){ 307 | int x = arr[dep].x; 308 | int y = arr[dep].y; 309 | if (canotPut(x, y, num)) continue; 310 | putNum(x, y, num); 311 | dfs(dep+1); 312 | removeNum(x, y, num); 313 | } 314 | } 315 | 316 | int main(){ 317 | A = 0; 318 | for (int i = 1; i <= 9; i++){ 319 | for (int j = 1; j <= 9; j++){ 320 | scanf("%d", &g[i][j]); 321 | if (g[i][j] == 0){ 322 | arr[++A] = Blank{i, j}; 323 | } else{ 324 | putNum(i, j, g[i][j]); 325 | } 326 | } 327 | } 328 | dfs(1); 329 | return 0; 330 | } 331 | ``` -------------------------------------------------------------------------------- /20-comMath/组合数51nod专题.md: -------------------------------------------------------------------------------- 1 | # 51nod组合数专题 2 | 3 | ## 大大走格子 4 | 5 | 测试网站[51nod 1486](https://www.51nod.com/Challenge/Problem.html#!#problemId=1486) 6 | 7 | ### 题目描述 8 | 9 | 有一个`h`行`w`列的棋盘,里面有一些格子是不能走的,现在要求从左上角走到右下角的方案数。 10 | 11 | ### 输入格式 12 | 13 | 单组测试数据。 14 | 15 | 第一行有三个整数`h`, `w`, `n`,表示棋盘的行和列,还有不能走的格子的数目。 16 | 17 | 接下来`n`行描述格子,第i行有两个整数`ri`, `ci`,表示格子所在的行和列。 18 | 19 | 输入保证起点和终点不会有不能走的格子。 20 | 21 | 22 | ### 输出格式 23 | 24 | 输出答案对`1000000007`取余的结果。 25 | 26 | ### 输入样例 27 | 28 | 3 4 2 29 | 2 2 30 | 2 3 31 | 32 | ### 输出样例 33 | 34 | 2 35 | 36 | ### 数据范围 37 | - `1 ≤ h, w ≤ 10^5` 38 | - `1 ≤ n ≤ 2000` 39 | - `1 ≤ ri ≤ h, 1 ≤ ci ≤ w` 40 | 41 | ### 题目分析 42 | 43 | 用`ans[i]`代表从起点走到第`i`个障碍格子且中间不经过其他障碍格子的方案数(终点也要算作障碍格子)。 44 | 45 | 所以一开始我们需要对障碍物点根据`x`,`y`坐标进行排序,那我们求解`ans[i]`的时候只要计算`(1,1)`走到`(ri,ci)`的方案数减去所有的`ans[j]*C(ri-rj+ci-cj,ri-rj)`,其中`1 ≤ j < i`。即减去经过前面障碍物的方案个数。 46 | 47 | `j`作为经过的第一个障碍物,后面无限制地随便走,这样保证了减去的方案不会重复也不会缺少。 48 | 49 | ### 代码示例 50 | 51 | ```c++ 52 | 53 | #include 54 | using namespace std; 55 | #define mst(a,b) memset((a),(b),sizeof(a)) 56 | #define rush() int T;scanf("%d",&T);while(T--) 57 | 58 | typedef long long ll; 59 | const int maxn = 200005; 60 | const ll mod = 1e9+7; 61 | const int INF = 0x3f3f3f3f; 62 | const double eps = 1e-9; 63 | 64 | int n,m,q; 65 | ll fac[maxn],inv[maxn]; 66 | ll ans[maxn]; 67 | 68 | struct node 69 | { 70 | int x,y; 71 | }a[maxn]; 72 | 73 | bool cmp(node a,node b) 74 | { 75 | if(a.x==b.x) return a.y=0; i--) 100 | { 101 | inv[i]=(inv[i+1]*(i+1))%mod; 102 | } 103 | } 104 | 105 | ll C(int n,int m) 106 | { 107 | if(n-m<0) return 0; 108 | return fac[n]*inv[m]%mod*inv[n-m]%mod; 109 | } 110 | 111 | ll cal(int s,int t) 112 | { 113 | return C(a[t].x-a[s].x+a[t].y-a[s].y,a[t].x-a[s].x); 114 | } 115 | 116 | int main() 117 | { 118 | init(); 119 | scanf("%d%d%d",&n,&m,&q); 120 | a[0].x=1,a[0].y=1; 121 | for(int i=1;i<=q;i++) 122 | { 123 | scanf("%d%d",&a[i].x,&a[i].y); 124 | } 125 | a[q+1].x=n,a[q+1].y=m; 126 | sort(a+1,a+q+2,cmp); 127 | 128 | for(int i=1;i<=q+1;i++) 129 | { 130 | ans[i]=cal(0,i); 131 | for(int j=1;j 163 | int dp[1001][1001],n,m,ans; 164 | int main() 165 | { 166 | for(int i=1;i<=1001;++i) 167 | { 168 | dp[1][i]=1; 169 | dp[i][1]=1; 170 | } 171 | scanf("%d%d",&m,&n); 172 | for(int i=2;i<=m;++i) 173 | { 174 | for(int j=2;j<=n;++j) 175 | { 176 | dp[i][j]=dp[i-1][j]+dp[i][j-1]; 177 | dp[i][j]%=1000000007; 178 | } 179 | } 180 | printf("%d",dp[m][n]); 181 | } 182 | ``` 183 | 当然也可以通过预处理快速地求组合数。总时间复杂度`O(n)` 184 | 185 | ### 代码示例2 186 | 187 | ```c++ 188 | #include 189 | using namespace std; 190 | #define mst(a,b) memset((a),(b),sizeof(a)) 191 | #define rush() int T;scanf("%d",&T);while(T--) 192 | 193 | typedef long long ll; 194 | const int maxn = 2005; 195 | const ll mod = 1e9+7; 196 | const int INF = 0x3f3f3f3f; 197 | const double eps = 1e-9; 198 | 199 | int n,m; 200 | ll fac[maxn],inv[maxn]; 201 | 202 | ll fast_mod(ll a,ll x,ll Mod) 203 | { 204 | ll ans=1; 205 | while(x) 206 | { 207 | if(x&1) ans=(ans*a)%Mod; 208 | a=(a*a)%Mod; 209 | x/=2; 210 | } 211 | return ans; 212 | } 213 | 214 | void init() //通过逆元预处理,以便O(1)求解大范围组合数 215 | { 216 | fac[0]=1; 217 | for(int i=1; i=0; i--) 223 | { 224 | inv[i]=(inv[i+1]*(i+1))%mod; 225 | } 226 | } 227 | 228 | ll C(int n,int m) 229 | { 230 | return fac[n]*inv[m]%mod*inv[n-m]%mod; 231 | } 232 | 233 | int main() 234 | { 235 | init(); 236 | scanf("%d%d",&n,&m); 237 | printf("%lld\n",C(n+m-2,n-1)); 238 | } 239 | 240 | ``` 241 | 242 | ## 机器人走方格V2 243 | 244 | 测试网站[51nod 1119](https://www.51nod.com/Challenge/Problem.html#!#problemId=1119) 245 | 246 | ### 题目描述 247 | 248 | `M` * `N` 的方格,一个机器人从左上走到右下,只能向右或向下走。有多少种不同的走法? 249 | 250 | 由于方法数量可能很大,只需要输出Mod `10^9 + 7`的结果。 251 | 252 | 253 | ### 数据范围 254 | - `2 ≤ N,M ≤ 1000000` 255 | 256 | 257 | 258 | ### 题目分析 259 | 260 | 根据题意,我们总共需要往右走`n-1`步,往下走`m-1`步,共`n+m-2`步,我们要做的其实就是在这`n+m-2`步中选出`m-1`个位置向右走即可。那么方案数即为`C(n+m-2,n-1)`。 261 | 262 | 跟之前的一题唯一的变化就是数据范围,这样我们显然不能用`O(n*n)`的方法求解,但还是可以用`O(n)`的方法二求解,改一下数据范围即可。 263 | 264 | ### 代码示例 265 | 266 | ```c++ 267 | #include 268 | using namespace std; 269 | #define mst(a,b) memset((a),(b),sizeof(a)) 270 | #define rush() int T;scanf("%d",&T);while(T--) 271 | 272 | typedef long long ll; 273 | const int maxn = 2000005; 274 | const ll mod = 1e9+7; 275 | const int INF = 0x3f3f3f3f; 276 | const double eps = 1e-9; 277 | 278 | int n,m; 279 | ll fac[maxn],inv[maxn]; 280 | 281 | ll fast_mod(ll a,ll x,ll Mod) 282 | { 283 | ll ans=1; 284 | while(x) 285 | { 286 | if(x&1) ans=(ans*a)%Mod; 287 | a=(a*a)%Mod; 288 | x/=2; 289 | } 290 | return ans; 291 | } 292 | 293 | void init() //通过逆元预处理,以便O(1)求解大范围组合数 294 | { 295 | fac[0]=1; 296 | for(int i=1; i=0; i--) 302 | { 303 | inv[i]=(inv[i+1]*(i+1))%mod; 304 | } 305 | } 306 | 307 | ll C(int n,int m) 308 | { 309 | return fac[n]*inv[m]%mod*inv[n-m]%mod; 310 | } 311 | 312 | int main() 313 | { 314 | init(); 315 | scanf("%d%d",&n,&m); 316 | printf("%lld\n",C(n+m-2,n-1)); 317 | } 318 | 319 | ``` 320 | 321 | ## 机器人走方格V3 322 | 323 | 测试网站[51nod 1120](https://www.51nod.com/Challenge/Problem.html#!#problemId=1120) 324 | 325 | ### 题目描述 326 | 327 | ` N * N `的方格,从左上到右下画一条线。一个机器人从左上走到右下,只能向右或向下走。 328 | 329 | 并要求只能在这条线的上面或下面走,不能穿越这条线,有多少种不同的走法? 330 | 331 | 由于方法数量可能很大,只需要输出Mod `10007`的结果。 332 | 333 | - `2 ≤ N ≤ 10^9` 334 | 335 | 336 | 337 | ### 题目分析 338 | 339 | 分析一下题意,我们可以这么转化,如果只能在这条线的下面走,相当于某一时刻向下走的次数要大于等于向右走的数量。 340 | 341 | 那么我们可以联想到这不就是括号匹配吗,用卡特兰数求解。即为`C(2n,n)`-`C(2n,n-1)`,由于可以往上面走或者下面走,最后答案乘以`2`即可。 342 | 343 | 由于这道题`n`的范围比较大,我们可以用`lucas`定理求组合数。 344 | 345 | ### 代码示例 346 | 347 | ```c++ 348 | #include 349 | using namespace std; 350 | #define mst(a,b) memset((a),(b),sizeof(a)) 351 | #define rush() int T;scanf("%d",&T);while(T--) 352 | 353 | typedef long long ll; 354 | const int maxn = 2000005; 355 | const ll mod = 1e4+7; 356 | const int INF = 0x3f3f3f3f; 357 | const double eps = 1e-9; 358 | 359 | int n; 360 | 361 | ll exgcd(ll a,ll b,ll &x,ll &y) 362 | { 363 | if(b==0) 364 | { 365 | x=1,y=0; 366 | return a; 367 | } 368 | ll d=exgcd(b,a%b,y,x); 369 | y-=a/b*x; 370 | return d; 371 | } 372 | 373 | ll Niyuan(ll a,ll n) 374 | { 375 | ll x,y; 376 | ll d=exgcd(a,n,x,y); 377 | if(d==1) 378 | return (x%n+n)%n; 379 | return -1; 380 | } 381 | 382 | ll muti(ll a,ll b) 383 | { 384 | return (a%mod)*(b%mod)%mod; 385 | } 386 | 387 | ll solve(ll n,ll m) 388 | { 389 | ll a=1,b=1; 390 | if(m>n) 391 | return 0ll; 392 | while(m) 393 | { 394 | a=(a*n)%mod; 395 | b=(b*m)%mod; 396 | m--; 397 | n--; 398 | } 399 | return muti(a,Niyuan(b,mod)); 400 | } 401 | 402 | ll lucas(ll n,ll m) 403 | { 404 | if(m==0) 405 | return 1ll; 406 | ll a=solve(n%mod,m%mod); 407 | ll b=lucas(n/mod,m/mod); 408 | return (a*b)%mod; 409 | } 410 | 411 | 412 | int main() 413 | { 414 | scanf("%d",&n); 415 | n--; 416 | printf("%lld\n",2*(lucas(2*n,n)-lucas(2*n,n-1)+mod)%mod); 417 | } 418 | 419 | 420 | ``` 421 | -------------------------------------------------------------------------------- /03-bigInteger/高精度算法.md: -------------------------------------------------------------------------------- 1 | /** 2 | * 国王游戏 3 | */ 4 | 5 | ```c++ 6 | #include 7 | using namespace std; 8 | typedef long long LL; 9 | const int N = 1003; 10 | const int MAXN = 6003; 11 | int B = 10; 12 | 13 | struct bign{ // big number 14 | int len, s[MAXN]; 15 | 16 | bign (){ 17 | memset(s, 0, sizeof(s)); 18 | len = 1; 19 | } 20 | bign (int num) { *this = num; } 21 | bign (const char *num) { *this = num; } 22 | 23 | bign operator = (const int num){ 24 | char s[MAXN]; 25 | sprintf(s, "%d", num); 26 | *this = s; 27 | return *this; 28 | } 29 | 30 | void clean(){ 31 | while(len > 1 && !s[len-1]) len--; 32 | } 33 | 34 | bign operator = (const char *num){ 35 | memset(s, 0, sizeof s); 36 | len = strlen(num); 37 | for(int i = 0; i < len; i++) { 38 | if (isdigit(num[len-i-1])) s[i] = num[len-i-1] - '0'; 39 | else s[i] = num[len-i-1] - 'A' + 10; 40 | } 41 | clean(); 42 | return *this; 43 | } 44 | 45 | bign operator + (const bign &b) const{ 46 | bign c; 47 | c.len = max(len, b.len); 48 | for (int i = 0; i < c.len; i++){ 49 | c.s[i] += s[i] + b.s[i]; 50 | c.s[i+1] += c.s[i] / B; // B default = 10 51 | c.s[i] %= B; 52 | } 53 | if (c.s[c.len]) c.len++; 54 | c.clean(); 55 | return c; 56 | } 57 | 58 | bign operator += (const bign &b){ 59 | *this = *this + b; 60 | return *this; 61 | } 62 | 63 | bign operator * (const bign &b) {//* 64 | bign c; 65 | c.len = len + b.len; 66 | for(int i = 0; i < len; i++) 67 | for(int j = 0; j < b.len; j++){ 68 | c.s[i+j] += s[i] * b.s[j]; 69 | } 70 | 71 | for(int i = 0; i < c.len; i++){ 72 | c.s[i+1] += c.s[i]/10; 73 | c.s[i] %= 10; 74 | } 75 | 76 | c.clean(); 77 | return c; 78 | } 79 | bign operator *= (const bign &b){ 80 | *this = *this * b; 81 | return *this; 82 | } 83 | bign operator - (const bign &b){ 84 | bign c; 85 | c.len = 0; 86 | for(int i = 0, g = 0; i < len; i++){ 87 | int x = s[i] - g; 88 | if(i < b.len) x -= b.s[i]; 89 | if(x >= 0) g = 0; 90 | else{ 91 | g = 1; 92 | x += 10; 93 | } 94 | c.s[c.len++] = x; 95 | } 96 | c.clean(); 97 | return c; 98 | } 99 | bign operator -= (const bign &b){ 100 | *this = *this - b; 101 | return *this; 102 | } 103 | bign operator / (const int &b){ 104 | int f = 0; 105 | bign c; 106 | for (int i = len-1; i >= 0; i--){ 107 | f = f *10 + s[i]; 108 | c.s[i] = f / b; 109 | f %= b; 110 | } 111 | c.len = len; 112 | c.clean(); 113 | return c; 114 | } 115 | bign operator / (const bign &b){ 116 | bign c, f = 0; 117 | for(int i = len-1; i >= 0; i--){ 118 | 119 | f = f*10; // f = 0 120 | f.s[0] = s[i]; // f = 1 121 | while(f > b || f == b){ 122 | 123 | f -= b; 124 | c.s[i]++; 125 | } 126 | } 127 | c.len = len; 128 | c.clean(); 129 | return c; 130 | } 131 | bign operator /= (const bign &b){ 132 | *this = *this / b; 133 | return *this; 134 | } 135 | bign operator % (const bign &b){ 136 | bign r = *this / b; 137 | r = *this - r*b; 138 | return r; 139 | } 140 | bign operator %= (const bign &b){ 141 | *this = *this % b; 142 | return *this; 143 | } 144 | 145 | bool operator < (const bign &b){ 146 | if(len != b.len) return len < b.len; 147 | for(int i = len-1; i >= 0; i--){ 148 | if(s[i] != b.s[i]) return s[i] < b.s[i]; 149 | } 150 | return false; 151 | } 152 | 153 | bool operator > (const bign &b){ 154 | if(len != b.len) return len > b.len; 155 | for(int i = len-1; i >= 0; i--){ 156 | if(s[i] != b.s[i]) return s[i] > b.s[i]; 157 | } 158 | return false; 159 | } 160 | 161 | bool operator == (const bign &b){ 162 | return !(*this > b) && !(*this < b); 163 | } 164 | 165 | string str() const{ 166 | string res = ""; // result 167 | for (int i = 0; i < len; i++) { 168 | if (s[i] < 10) res = char(s[i]+'0') + res; 169 | else res = char(s[i] + 'A' - 10) + res; 170 | } 171 | return res; 172 | } 173 | } a, b, c; 174 | ``` 175 | 176 | 177 | 178 | ## A+B Problem(高精) 179 | 180 | [luogu1601](https://www.luogu.org/problemnew/show/P1601) 181 | 182 | ```c++ 183 | bign operator + (const bign &b) const{ 184 | bign c; 185 | c.len = max(len, b.len); 186 | for (int i = 0; i < c.len; i++){ 187 | c.s[i] += s[i] + b.s[i]; 188 | c.s[i+1] += c.s[i] / B; // B default = 10 189 | c.s[i] %= B; 190 | } 191 | if (c.s[c.len]) c.len++; 192 | c.clean(); 193 | return c; 194 | } 195 | ``` 196 | 197 | main函数如同a+b 198 | 199 | ```c++ 200 | int main(){ 201 | bign a, b, c; 202 | while(scanf("%s%s", s1, s2) != EOF){ 203 | a = bign(s1); 204 | b = bign(s2); 205 | c = a + b; 206 | cout << c.str() << endl; 207 | } 208 | return 0; 209 | } 210 | ``` 211 | 212 | ## P2142 高精度减法 213 | 214 | [luogu2142](https://www.luogu.org/problemnew/show/P2142) 215 | 216 | 考察减法,不够的向高位进位 217 | 218 | ```c++ 219 | bign operator - (const bign &b){ 220 | bign c; 221 | c.len = 0; 222 | for(int i = 0, g = 0; i < len; i++){ 223 | int x = s[i] - g; 224 | if(i < b.len) x -= b.s[i]; 225 | if(x >= 0) g = 0; 226 | else{ 227 | g = 1; 228 | x += 10; 229 | } 230 | c.s[c.len++] = x; 231 | } 232 | c.clean(); 233 | return c; 234 | } 235 | ``` 236 | 237 | 238 | ## P1303 A*B Problem 239 | 240 | [luogu1303](https://www.luogu.org/problemnew/show/P1303) 241 | 242 | 考察乘法 243 | 244 | ```c++ 245 | bign operator * (const bign &b) {//* 246 | bign c; 247 | c.len = len + b.len; 248 | for(int i = 0; i < len; i++) 249 | for(int j = 0; j < b.len; j++){ 250 | c.s[i+j] += s[i] * b.s[j]; 251 | } 252 | 253 | for(int i = 0; i < c.len; i++){ 254 | c.s[i+1] += c.s[i]/10; 255 | c.s[i] %= 10; 256 | } 257 | 258 | c.clean(); 259 | return c; 260 | } 261 | ``` 262 | 263 | ## P1255 数楼梯 264 | 265 | [luogu1255](https://www.luogu.org/problemnew/show/P1255) 266 | 267 | 其实就是斐波那契辣 268 | 269 | ```c++ 270 | int main() 271 | { 272 | //freopen("in.txt", "r", stdin); 273 | int n; 274 | scanf("%d", &n); 275 | f[0].len = 1; f[0].num[1] = 0; 276 | f[1].len = 1; f[1].num[1] = 1; 277 | f[2].len = 1; f[2].num[1] = 2; 278 | 279 | for (int i = 3; i <= n; i++){ 280 | f[i%3] = f[(i+1)%3] + f[(i+2)%3]; 281 | } 282 | f[n%3].print(); 283 | return 0; 284 | } 285 | ``` 286 | 287 | ##P1604 B进制星球 288 | 289 | 修改后的加法 290 | 291 | ```c++ 292 | bign operator + (const bign &b) const{ 293 | bign c; 294 | c.len = max(len, b.len); 295 | for (int i = 0; i < c.len; i++){ 296 | c.s[i] += s[i] + b.s[i]; 297 | c.s[i+1] += c.s[i] / B; // B default = 10 298 | c.s[i] %= B; 299 | } 300 | if (c.s[c.len]) c.len++; 301 | c.clean(); 302 | return c; 303 | } 304 | 305 | ``` 306 | 307 | main反而很简单 308 | 309 | ```c++ 310 | int main(){ 311 | while(~scanf("%d", &B)){ 312 | scanf("%s%s", s1, s2); 313 | a = bign(s1); 314 | b = bign(s2); 315 | c = a + b; 316 | cout << c.str() << endl; 317 | } 318 | return 0; 319 | } 320 | ``` 321 | 322 | ## 国王游戏 323 | 324 | 不用高精度最后两个点会炸,本题在贪心专题会进一步分析 325 | 326 | ```c++ 327 | struct people{ 328 | int l, r, mon; 329 | 330 | void scan(int i){ 331 | scanf("%d %d", &l, &r); 332 | mon = r * l; 333 | } 334 | 335 | bool operator < (const people &b)const{ 336 | if(mon != b.mon) return mon < b.mon; 337 | return r < b.r; 338 | } 339 | }peo[N]; 340 | 341 | int main() { 342 | //freopen("in.txt", "r", stdin); 343 | int n; 344 | scanf("%d", &n); 345 | for(int i = 0; i <= n; i++){ 346 | peo[i].scan(i); 347 | } 348 | 349 | sort(peo + 1, peo + 1+n); 350 | 351 | bign mone = peo[0].l, ans = 0; 352 | for(int i = 1; i <= n; i++){ 353 | //bign t = bign(); 354 | bign tmp = (mone / peo[i].r); 355 | if (tmp > ans) ans = tmp; 356 | mone *= bign(peo[i].l); 357 | } 358 | cout << ans.str() << endl; 359 | return 0; 360 | } 361 | ``` -------------------------------------------------------------------------------- /10-graphAlgorithm1/并查集.md: -------------------------------------------------------------------------------- 1 | # 并查集 2 | 3 | 并查集是一种用来管理元素分组情况的数据结构,并查集可以高效的进行如下操作 4 | - 查询元素a和元素b 是否属于同一组 5 | - 合并元素a和元素b 所在的组 6 | 7 | ## 并查集的基本实现 8 | 9 | ```c++ 10 | int par[maxn]; 11 | void init(int n) 12 | { 13 | for(int i=0;i 56 | #include 57 | #include 58 | using namespace std; 59 | const int maxx=30000+50; 60 | int ji[maxx]; 61 | int cun[maxx]; 62 | int father[maxx]; 63 | int findnum(int x) 64 | { 65 | if(x!=father[x]) 66 | { 67 | int root= findnum(father[x]); 68 | cun[x]+=cun[father[x]]; 69 | return father[x]=root; 70 | } 71 | else { 72 | return x; 73 | } 74 | } 75 | 76 | void init() 77 | { 78 | for(int i=0; i<=maxx; i++)father[i]=i,ji[i]=0; 79 | memset(cun,0,sizeof(cun)); 80 | } 81 | 82 | int main() 83 | { 84 | ios_base::sync_with_stdio(false); 85 | int p,a,b; 86 | cin>>p; 87 | char c; 88 | init(); 89 | while(p--) 90 | { 91 | cin>>c; 92 | if(c=='M') 93 | { 94 | cin>>a>>b; 95 | int b1=findnum(b); 96 | int a1=findnum(a); 97 | father[b1]=a1; 98 | cun[b1]=ji[a1]+1; 99 | ji[a1]=ji[a1]+ji[b1]+1; 100 | } 101 | else 102 | { 103 | cin>>a; 104 | int root=findnum(a); 105 | cout<B->C;如果A->B=0;B->C=1的话,那么A->C就等于(0+1)%3=1;即A,B同类,都可以吃C。实际上可以看做向量的形式。当我们知道x与祖先x1的关系,y与祖先y1的关系,x与y的关系时,求x1与y1的关系,使用矢量 计算: x1->x ->y ->y1 141 | 142 | ### 代码 143 | 144 | ```c++ 145 | #include 146 | #include 147 | #include 148 | #include 149 | using namespace std; 150 | const int maxx=50000+50; 151 | int father[maxx],value[maxx]; 152 | int n,k; 153 | void init(){ 154 | for(int i=1;i<=n;i++) 155 | father[i]=i,value[i]=0; 156 | } 157 | 158 | int findfather(int x) 159 | { 160 | if(father[x]==x)return x; 161 | int y=findfather(father[x]); 162 | value[x]=(value[x]+value[father[x]]+3)%3; 163 | return father[x]=y;//路径压缩 164 | } 165 | 166 | int unino(int type,int x,int y) 167 | { 168 | int x1=findfather(x); 169 | int y1=findfather(y); 170 | if(x1==y1){ 171 | if((value[x]-value[y]+3)%3==type-1)return 0; 172 | else return 1; 173 | } 174 | father[x1]=y1; 175 | value[x1]=(3-value[x]+value[y]+type-1)%3; 176 | return 0; 177 | } 178 | 179 | int main(){ 180 | ios_base::sync_with_stdio(false); 181 | int typ=0,xx=0,yy=0,ans=0; 182 | scanf("%d%d",&n,&k); 183 | init(); 184 | while(k--) 185 | { 186 | scanf("%d%d%d",&typ,&xx,&yy); 187 | if(xx==yy&&typ!=1)ans++; 188 | else if(xx>n||yy>n)ans++; 189 | else ans+=unino(typ,xx,yy); 190 | } 191 | printf("%d\n",ans); 192 | return 0;} 193 | 194 | ``` 195 | 196 | 197 | 198 | ## The Suspects 199 | 200 | [POJ 1611](http://poj.org/problem?id=1611) 201 | 202 | ### 题目大意 203 | 204 | 为了控制非典病毒的传播,我们需要把感染者隔离,现在给出若干个学生为一组,他们之间接触频繁,如果有一个人被非典病毒感染了,那么整个组的同学都被感染了,让你找出所有的被感染者 205 | 206 | 207 | 208 | ### 题目分析 209 | 210 | 这是一个裸的并查集的题目,直接把每一个小组的成员的根节点的父亲节点赋值到小组第一个成员上,并需要给每个集合计数。初始时,各个集合都是一个元素,就是他们本身,然后开始合并,合并的时候,要把合并的子集的数目加到父集合当中。 211 | 212 | 213 | 214 | ### 代码 215 | 216 | ```c++ 217 | #include 218 | #include 219 | #include 220 | #include 221 | using namespace std; 222 | const int maxx=30000+50; 223 | int father[maxx],cun[maxx]; 224 | int n,m; 225 | int findfather(int x){ 226 | return x==father[x]? x:findfather(father[x]); 227 | } 228 | 229 | void init(){ 230 | for(int i=0;i<=n;i++)father[i]=i,cun[i]=1;//初始集合数量和各集合元素 231 | } 232 | 233 | int main(){ 234 | int num,a,b; 235 | while(scanf("%d%d",&n,&m)==2&&n) 236 | { 237 | init(); 238 | while(m--) 239 | { 240 | if(m<0)break; 241 | scanf("%d%d",&num,&a); 242 | int aa=findfather(a); 243 | for(int i=0;i 278 | #include 279 | #include 280 | using namespace std; 281 | const int maxx=100050; 282 | int father[maxx]; 283 | int findfather(int x) 284 | { 285 | return father[x]==x?x:findfather(father[x]); 286 | } 287 | 288 | void init(){ 289 | for(int i=0;i>a) 297 | { 298 | if(a==-1){//注意是多组数据 299 | cout<>b; 305 | int aa=findfather(a); 306 | int bb=findfather(b); 307 | if(aa==bb)flag++; 308 | else father[aa]=bb; 309 | } 310 | return 0;} 311 | 312 | 313 | ``` 314 | 315 | 316 | 317 | ## Find them, Catch them 318 | 319 | [POJ 1703](http://poj.org/problem?id=1703) 320 | 321 | 322 | 323 | ### 题目大意 324 | 325 | 黑道中有两大帮派,龙帮和蛇帮,输入一下信息 326 | 327 | - D a b ( 表示 a和b不在同一个帮派) 328 | - A a b(需要你判断a b 两人人是否属于同一帮派) 329 | 330 | 331 | 332 | ### 题目分析 333 | 334 | 其实总的来说就是食物链的简化版本,把食物链中的三种关系简化成了两种关系。做法是一样的,用0来表示同一帮派,1来表示不同帮派。那么如果字母是d,那么连接两个根节点就可以用((cun[a]+cun[b]+1)%2),cun数组存的是节点和其父亲节点的关系 335 | 336 | 注意:这个题目不能用cin 337 | 338 | 339 | 340 | ### 代码 341 | 342 | ```c++ 343 | #include 344 | #include 345 | #include 346 | #include 347 | using namespace std; 348 | const int maxx=100000+50; 349 | int fa[maxx],cun[maxx]; 350 | int n,m; 351 | 352 | int findnum(int x) 353 | { 354 | if(x==fa[x])return x; 355 | else{ 356 | int root=findnum(fa[x]); 357 | cun[x]=(cun[x]+cun[fa[x]])%2; 358 | return fa[x]=root; 359 | } 360 | } 361 | 362 | void init(){ 363 | for(int i=0;i<=n;i++)fa[i]=i; 364 | memset(cun,0,sizeof(cun)); 365 | } 366 | 367 | 368 | int main(){ 369 | int t,a,b;char c[2]; 370 | scanf("%d",&t); 371 | while(t--) 372 | { 373 | scanf("%d%d",&n,&m); 374 | init(); 375 | while(m--) 376 | { 377 | scanf("%s%d%d",c,&a,&b); 378 | int a1=findnum(a); 379 | int b1=findnum(b); 380 | if(c[0]=='D') 381 | {//he 382 | fa[b1]=a1; 383 | cun[b1]=(cun[a]+cun[b]+1)%2; 384 | } 385 | else { 386 | if(a1!=b1)cout<<"Not sure yet."< 32 | #include 33 | #include 34 | #include 35 | using namespace std; 36 | #define Max 110 37 | #define INF 0x3f3f3f3f 38 | int c; 39 | int n; 40 | int n1, n5, n10; 41 | int dp[800][200][100];//dp[i][j][k]表示面值为1、5、10的硬币剩余i、j、k个时,投入的硬币数量的最小值 42 | 43 | int solve(int n, int n1, int n5, int n10) 44 | { 45 | if(n == 0)return 0;//当买完可乐时返回0 46 | if(dp[n1][n5][n10] != INF) 47 | { 48 | return dp[n1][n5][n10];//此状态已经搜过,返回该值 49 | } 50 | int ans = INF; 51 | int now; 52 | if(n10 >= 1) 53 | {//投一个面值10,面值1加2个,需要投一次币 54 | now = solve(n-1, n1+2, n5, n10-1) + 1; 55 | ans = min(ans, now); 56 | } 57 | if(n1 >= 3 && n10 >= 1) 58 | {//投一个面值10和三个面值1,面值5加1个,需要投四次币 59 | now = solve(n-1, n1-3, n5+1, n10-1) + 4; 60 | ans = min(ans, now); 61 | } 62 | if(n5 >= 2) 63 | {//投两个面值5,面值1加2个,需要投两次币 64 | now = solve(n-1, n1+2, n5-2, n10) + 2; 65 | ans = min(ans, now); 66 | } 67 | if(n5 >= 1 && n1 >= 3) 68 | {//投一个面值5和三个面值1,需要投四次币 69 | now = solve(n-1, n1-3, n5-1, n10) + 4; 70 | ans = min(ans, now); 71 | } 72 | if(n1 >= 8) 73 | {//投一个面值1,需要投八次币 74 | now = solve(n-1, n1-8, n5, n10) + 8; 75 | ans = min(ans, now); 76 | } 77 | dp[n1][n5][n10] = ans; 78 | return ans; 79 | } 80 | int main() 81 | { 82 | int numberofcase; 83 | scanf("%d", &numberofcase); 84 | while(numberofcase--) 85 | { 86 | scanf("%d%d%d%d", &n, &n1, &n5, &n10); 87 | memset(dp, INF, sizeof(dp)); 88 | cout << solve(n, n1, n5, n10) << endl; 89 | } 90 | return 0; 91 | } 92 | ``` 93 | 94 | 95 | ## Chemical Reaction 96 | 97 | 测试网站[uva 10604](https://vjudge.net/problem/UVA-10604) 98 | 99 | ### 题目描述 100 | In a chemists lab, there are several types of chemicals in tubes. The chemist wants to mix all these 101 | chemicals together, two chemicals at a time. Whenever two chemicals are mixed, some heat is generated 102 | and released into the air and the mixed chemical is a known chemical of possibly other type than the 103 | original two. The resulting chemical type and the amount of heats emitted can looked up in the chemical 104 | mixture table. 105 | ![](images/uva10604.PNG) 106 | For example, in the above chemical mixture table, there are three types of chemicals: 1, 2, and 3. If 107 | you mix chemicals 1 and 3, they produce +3000 units of heat and turn into chemical 3. Sometimes, the 108 | heat generated can be negative. For instance, you can mix 2 and 3 and they turn into chemical 1 and in 109 | the meantime, cool down the lab by 500 units of heat. Since the chemist lacks funding to buy necessary 110 | equipments to protect himself from the heat generated, it is utmost important to find a way to mix all the 111 | chemicals together that produces the least total heat, regardless of the final chemical type. For example, 112 | suppose the lab has four tubes containing chemicals of types 1, 2, 2, and 3. If the chemicals are mixed 113 | in the parenthesize order of ((12)(23)), it will produce (−10) + (−500) + (3000) = 2490 units of heat. 114 | However, if the chemicals are mixed in the (2(1(23))) order, it will produce (−500) + 0 + (−10) = −510 115 | units of heat, which is also the least total heat possible. 116 | 117 | ### 题目翻译 118 | 119 | 有m种化学原料,原料两两融合会产生新的原料和释放热量,现给出反应的式子和热量的变化,还有k个试管存放的原料,问怎样融合使得产生的热量最少? 120 | 121 | ### 题目分析 122 | 123 | 深度优先搜索,首先考虑暴力解法:把所有原料融合顺序的可能都枚举一遍,得到最小值。优化:对于已经搜索过的状态,将其值保存下来, 当搜索到该状态时,不需要再继续深搜而是直接返回该值。 124 | 125 | 深搜过程:将原料两两枚举,只要该原料数量大于0,则将其融合,直到所有原料数量为0,返回每种顺序的融合产生的热量的最小值。 126 | 127 | 实现:可以定义一个`a[]`数组保存每种原料的数目,`dp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]]`六维数组(原料种数最多不超过6种) 128 | 用来表示六种原料当还剩`a[i]`个时产生的最小的热量。 129 | 130 | ### 代码示例 131 | ```c++ 132 | #include 133 | using namespace std; 134 | const int SZ = 12; 135 | int g_n, g_k; 136 | int c[SZ][SZ], d[SZ][SZ];//c[i][j]保存i和j融合生产的化学原料,d[i][j]保存i和j融合释放的热量 137 | int n[SZ]; 138 | int a[SZ];//a[i]保存i原料的数目 139 | int dp[SZ][SZ][SZ][SZ][SZ][SZ];//保存当每种原料还剩a[i]时产生的热量 140 | void input() {//输入及初始化 141 | memset(dp, -1, sizeof(dp)); 142 | memset(a, 0, sizeof(a)); 143 | cin >> g_n; 144 | for(int i = 1; i <= g_n; ++i) { 145 | for(int j = 1; j <= g_n; ++j) { 146 | scanf("%d%d", &c[i][j], &d[i][j]); 147 | } 148 | } 149 | cin >> g_k; 150 | for(int i = 1; i <= g_k; ++i) { 151 | scanf("%d", &n[i]); 152 | ++a[n[i]];//统计i原料的数目 153 | } 154 | } 155 | int dfs(int k) { 156 | if(dp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]] != -1)//如果该状态已经搜索过就直接返回值 157 | return dp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]]; 158 | if(k == 1)return 0;//如果只剩一个原料返回0 159 | int ct = INT_MAX; 160 | for(int i = 1; i <= g_n; ++i) {//搜索所有融合情况 161 | for(int j = 1; j <= g_n; ++j) { 162 | if(i == j && a[i] < 2)continue;//如果数量不够两个则跳过 163 | if(a[i] > 0 && a[j] > 0) { 164 | --a[i], --a[j], ++a[c[i][j]];//够的话,让每种原料数量减一,融合生成的原料数量加一 165 | ct = min(ct, dfs(k - 1) + d[i][j]);//热量加上i和j融合释放的热量 166 | ++a[i], ++a[j], --a[c[i][j]]; 167 | } 168 | } 169 | } 170 | return dp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]] = ct; 171 | } 172 | int main() { 173 | int t; 174 | cin >> t; 175 | while(t --) { 176 | input(); 177 | printf("%d\n", dfs(g_k)); 178 | char ch; 179 | cin >> ch; 180 | } 181 | return 0; 182 | } 183 | ``` 184 | 185 | ## Free Candies 186 | 187 | 测试网站[uva 10118](https://vjudge.net/problem/UVA-10118) 188 | 189 | ### 题目描述 190 | 191 | Little Bob is playing a game. He wants to win some candies in it - as many as possible. 192 | There are 4 piles, each pile contains N candies. Bob is given a basket which can hold at most 5 193 | candies. Each time, he puts a candy at the top of one pile into the basket, and if there’re two candies 194 | of the same color in it, he can take both of them outside the basket and put them into his own pocket. 195 | When the basket is full and there are no two candies of the same color, the game ends. If the game is 196 | played perfectly, the game will end with no candies left in the piles. 197 | For example, Bob may play this game like this (N = 5): 198 | ![](images/uva10118.PNG) 199 | Note that different numbers indicate different colors, there are 20 kinds of colors numbered 1..20. 200 | ‘Seems so hard...’ Bob got very much puzzled. How many pairs of candies could he take home at 201 | most? 202 | 203 | ### 题目翻译 204 | 205 | 有四堆糖果,每堆n个,每颗糖果都有颜色。BOb每次可以任选一堆从堆顶拿出一个放入篮子,篮子最多只能放五颗糖果,但当篮子中出现颜色一样的糖果时,Bob可以将这两个颜色一样的糖果拿出篮子放入自己的口袋里,问BOb最多可以拿到多少对糖果? 206 | 207 | ### 题目分析 208 | 209 | 深度优先搜索,首先考虑暴力解法:把所有拿糖果的可能都枚举一遍,得到最大值。优化:对于已经搜索过的状态,将其值保存下来,当搜索到该状态时,不需要再继续深搜而是直接返回该值。 210 | 211 | 深搜过程:四堆,每次有四种选择,选出一个糖果后有两种情况:篮子里有这个颜色,则答案加一,篮子里糖果数减一; 212 | 213 | 没有这个颜色,放入篮子,答案不变,糖果数加一。直到篮子里糖果数为5或者所有糖果拿完,深搜结束。 214 | 215 | 实现:可以定义一个a[]数组表示第i堆选择到了第几个,dp[a[0]][a[1]][a[2]][a[3]]四维数组用来表示四堆糖果当拿到第a[i]个时得到的最多的对数,可以再定义一个vis[]数组表示该颜色是否已在篮子里,若vis[i]为true表示该颜色在篮子里则拿出来,答案加一,然后将其置为false,若不在将vis[i]置为true。 216 | 217 | ### 代码示例 218 | 219 | ```c++ 220 | #include 221 | using namespace std; 222 | const int SZ = 50; 223 | int n; 224 | int candy[4][SZ];//保存四堆糖果 225 | int dp[SZ][SZ][SZ][SZ];//表示1 2 3 4 堆分别选到了哪一个最多的对数 226 | int a[4];//记录每堆选择到了第几个 227 | bool vis[SZ];//记录该颜色是否在篮子里 228 | void input() {//输入及初始化 229 | memset(vis, 0, sizeof(vis)); 230 | memset(a, 0, sizeof(a)); 231 | memset(dp, -1, sizeof(dp)); 232 | for(int i = 0; i < n; ++i) { 233 | for(int j = 0; j < 4; ++j) { 234 | scanf("%d", &candy[j][i]); 235 | } 236 | } 237 | } 238 | int dfs(int cnt) { 239 | if(dp[a[0]][a[1]][a[2]][a[3]] != -1) {//如果该状态已经搜索过就直接返回值 240 | return dp[a[0]][a[1]][a[2]][a[3]]; 241 | } 242 | if(cnt == 5) {//如果篮子里糖果数为5则返回0 243 | return dp[a[0]][a[1]][a[2]][a[3]] = 0; 244 | } 245 | int ans = 0; 246 | for(int i = 0; i < 4; ++i) { 247 | if(a[i] == n)continue;//如果第i堆已经选择到第n个,则选择跳过 248 | int c = candy[i][a[i]]; 249 | ++a[i]; 250 | if(vis[c]) {//如果篮子里有这个颜色的糖果,答案加一 251 | vis[c] = false; 252 | ans = max(ans, dfs(cnt - 1) + 1); 253 | vis[c] = true; 254 | } else {//如果有这个颜色的糖果,放入篮子 255 | vis[c] = true; 256 | ans = max(ans, dfs(cnt + 1)); 257 | vis[c] = false; 258 | } 259 | --a[i]; 260 | } 261 | return dp[a[0]][a[1]][a[2]][a[3]] = ans; 262 | } 263 | 264 | 265 | int main() { 266 | while (cin >> n && n) { 267 | input(); 268 | printf("%d\n", dfs(0)); 269 | } 270 | return 0; 271 | } 272 | ``` 273 | -------------------------------------------------------------------------------- /20-comMath/容斥.md: -------------------------------------------------------------------------------- 1 | # 容斥专题 2 | 3 | ![](images/Tolerance1.png) 4 | 5 | ![](images/Tolerance2.png) 6 | 7 | ## 整数的力量(Integer Power) 8 | 9 | [HDU]() 10 | 11 | **tags: 容斥,高精度开根号** 12 | 13 | ### 题意 14 | 15 | 在一个区间$[l,r]$,其中每一个数必定可以写成写成$a^p$的形式,现在设$p$的和为$res$,问你在$[l,r]$区间内最大的$res$为多少。 16 | 17 | ### 思路 18 | 19 | 首先,不难发现,对于一段区间而言,区间某个数的幂的个数存在着累加性。即倘若我们需要求区间[l,r]的结果,我们只需要先求出区间$[1,r]​$的结果$res1​$,再求出区间$[1,l-1]​$的结果$res2​$,之后用$res1-res2​$即为区间[l,r]的结果。 20 | 21 | 之后题目就转化为区间内$a^k$的个数问题,显然有$\lfloor n^{\frac{1}{k}}\rfloor$种,因此我们枚举$k$ 22 | 23 | 但是,这么做显然是会重复计数的,如$2^4 = 4^2 = ...$因此我们还需要把重复的部分根据容斥原理剪掉。因为题目中要求我们使得结果最大,因此我们需要尽可能的让幂数大的保留下来,也就是说,幂数较小的需要排斥掉。因此我们可以考虑最后从大到小枚举幂数,如果发现两个幂数$(i \mod j == 0 , i>j)$我们就把$j$中$j$和$i$相同的部分抹去。 24 | 25 | ### 代码 26 | 27 | ```C++ 28 | #include 29 | using namespace std; 30 | typedef long long ll; 31 | ll sum[80]; 32 | const double eps=1e-18; 33 | const ll inf = (ll)(1e18) + 500; 34 | const ll INF = (ll)1 << 31; 35 | 36 | ll multi(ll a,ll b){ 37 | ll ans=1; 38 | while(b){ 39 | if(b&1){ 40 | double judge=1.0*inf/ans; 41 | if(a>judge) return -1; 42 | ans*=a; 43 | } 44 | b>>=1; 45 | if(a>INF&&b>0) return -1; 46 | a=a*a; 47 | } 48 | return ans; 49 | } 50 | ll Find(ll x,ll k){ 51 | ll r=(ll)pow(x,1.0/k); 52 | ll t,p; 53 | p=multi(r,k); 54 | if(p==x) return r; 55 | if(p>x||p==-1) r--; 56 | else 57 | { 58 | t=multi(r+1,k); 59 | if(t!=-1&&t<=x) r++; 60 | } 61 | return r; 62 | } 63 | 64 | ll solve(ll n){ 65 | sum[1]=n; 66 | int maxx=0; 67 | for(int i=2;i<64;i++){ 68 | sum[i]=Find(n,1ll*i); 69 | sum[i]--; 70 | if(sum[i]==0){ 71 | maxx=i; 72 | break; 73 | } 74 | } 75 | for(int i=maxx;i>=2;i--){ 76 | for(int j=1;j>a>>b){ 90 | if(a==0) break; 91 | ll ans=solve(b)-solve(a-1); 92 | cout<) 101 | 102 | **Tags: 数论,容斥,莫比乌斯反演,欧拉函数** 103 | 104 | ### 题意 105 | 106 | 求$(1,b)$区间和$(1,d)$区间里面$gcd(x, y) = k$的数的对数$(1 \le x \le b , 1\le y \le d)$。 107 | 108 | ### 思路 109 | 110 | 只需要枚举$x$,然后确定另一个区间里面有多少个$y$就可以了。因此问题转化成为区间$(1,d)​$里面与x互素的数的个数 111 | 112 | 先求出x的所有质因数,因此$(1,d)$区间里面是x的质因数倍数的数都不会与$x​$互素,因此,只需要求出这些数的个数,减掉就可以了。 113 | 114 | 如果w是x的素因子,则$(1,d)$中是$w$倍数的数共有$\frac{d}{w}​$个。 115 | 116 | ### 代码 117 | 118 | ```C++ 119 | #include 120 | #include 121 | #include 122 | #include 123 | using namespace std; 124 | #define N 100005 125 | typedef long long ll; 126 | vector x[N]; 127 | bool is[N]; 128 | 129 | void prime() { 130 | memset(is, false, sizeof(is)); 131 | for (int i=0; i d) { a = b; b = d; d = a; } 170 | long long ans = 0; 171 | for (int i=1; i<=d; i++) { 172 | k = min(i, b); 173 | ans += k; 174 | for (int j=1; j<(1< 203 | #include 204 | #include 205 | #include 206 | #include 207 | #include 208 | #include 209 | #include 210 | #include 211 | #include 212 | using namespace std; 213 | typedef double db; 214 | template inline bool updateMin(T& a, T b) { return a>b? a=b, true: false; } 215 | template inline bool updateMax(T& a, T b) { return a=1000) 232 | { 233 | digit[i+1]=digit[i]/1000+digit[i+1]; 234 | digit[i]=digit[i]%1000; 235 | } 236 | i++; 237 | } 238 | while(digit[i]) 239 | { 240 | if(digit[i]>=1000) 241 | { 242 | digit[i+1]=digit[i]/1000; 243 | digit[i]=digit[i]%1000; 244 | lenth++; 245 | } 246 | i++; 247 | } 248 | } 249 | bign(){memset(digit,0,sizeof(digit));lenth=1;} 250 | bign operator * (const bign& b)const 251 | { 252 | bign c; 253 | for(int i=0;i=0;i--) 316 | { 317 | printf("%03d",ans.digit[i]); 318 | } 319 | puts(""); 320 | return 0; 321 | } 322 | ``` 323 | 324 | 325 | 326 | ## Co-Prime 327 | 328 | [HDU-4135]() 329 | 330 | **Tags: 容斥** 331 | 332 | ### 题意 333 | 334 | 从区间$[a,b]$中找到与$n$互质的数的个数 335 | 336 | ### 思路 337 | 338 | - 可以通过求出区间内与N互质数的个数的前缀和,即$[1,X]$,来得出$[A,B]$。 339 | - 那么现在问题是求出$[1,X]​$区间内与$N​$互质数的个数,考虑这个问题的逆问题:$[1,X]​$区间内与$N​$不互质数的个数。 340 | - 于是就可以先处理出N的所有质因数${p_0,p_1,p_2,...,p_n}​$。 341 | - 而$[1,X]$能被$p_i$整除的数有$\frac{X}{p_i}$个,再利用容斥原理除掉质因数公倍数重复计数的部分就能求出不互质个数。 342 | - 最后X减去不互质个数就是互质个数了。 343 | 344 | ### 代码 345 | 346 | ```C++ 347 | #include 348 | #include 349 | using namespace std; 350 | int prime[33],pn; 351 | long long calc(long long n){ 352 | long long res=0; 353 | for(int i=1; i<(1<>j)&1)==0) continue; 357 | ++cnt; 358 | tmp*=prime[j]; 359 | } 360 | if(cnt&1) res+=n/tmp; 361 | else res-=n/tmp; 362 | } 363 | return n-res; 364 | } 365 | int main(){ 366 | long long a,b; 367 | int t,n; 368 | scanf("%d",&t); 369 | for(int cse=1; cse<=t; ++cse){ 370 | scanf("%lld%lld%d",&a,&b,&n); 371 | pn=0; 372 | for(int i=2; i*i<=n; ++i){ 373 | if(n%i) continue; 374 | while(n%i==0) n/=i; 375 | prime[pn++]=i; 376 | } 377 | if(n!=1) prime[pn++]=n; 378 | printf("Case #%d: %lld\n",cse,calc(b)-calc(a-1)); 379 | } 380 | return 0; 381 | } 382 | ``` 383 | 384 | -------------------------------------------------------------------------------- /07-searchOptimization/dfs优化.md: -------------------------------------------------------------------------------- 1 | # DFS优化 2 | 3 | ## 狗哥玩木棒 4 | 5 | [洛谷 P2383](https://www.luogu.org/problemnew/show/P2383 "洛谷P2383 狗哥玩木棒") 6 | 7 | ### 题目大意: 8 | 9 | 给出n个木棒的长度,问这些木棒能否拼凑成为一个正方形 10 | 11 | ### 题目分析: 12 | 13 | 这个题目,首先是要拼成正方形,所以,我们可以确定每一条边的长度应该是总长度除以4。然后,我们就进行搜索,由于我们知道每一条边的长度,当某一条边上的值大于答案,那么这个答案就不需要考虑了。就可以对这种情况进行剪枝。 14 | 15 | ### 代码 16 | ```c++ 17 | #include 18 | using namespace std; 19 | const int maxn=1e5+50; 20 | int n; 21 | int m,eva; 22 | int s[maxn]; 23 | int flag; 24 | bool cmp(int a,int b){return a>b;} 25 | void dfs(int k,int a,int b,int c,int d) 26 | { 27 | if(a>eva||b>eva||c>eva||d>eva)return; 28 | if(a==eva&&b==eva&&c==eva&&d==eva) flag=1; 29 | if(flag==1)return; 30 | dfs(k+1,a+s[k],b,c,d); 31 | dfs(k+1,a,b+s[k],c,d); 32 | dfs(k+1,a,b,c+s[k],d); 33 | dfs(k+1,a,b,c,d+s[k]); 34 | } 35 | int main() 36 | { 37 | scanf("%d",&n); 38 | int all=0; 39 | while(n--) 40 | { 41 | scanf("%d",&m); 42 | all=0; 43 | memset(s,0,sizeof(s)); 44 | for(int i=1;i<=m;i++) {scanf("%d",&s[i]);all+=s[i];} 45 | if(all%4) {puts("no");continue;} 46 | eva=all/4; 47 | sort(s+1,s+1+m,cmp); 48 | flag=0; 49 | dfs(1,0,0,0,0); 50 | if(flag)puts("yes"); 51 | else puts("no"); 52 | 53 | } 54 | return 0; 55 | } 56 | ``` 57 | 58 | 59 | 60 | ## 小木棍[数据加强版] 61 | 62 | [洛谷 P1120](https://www.luogu.org/problemnew/show/P1120) 63 | 64 | ### 题目大意 65 | 66 | 把一些长的木棍随意砍成若干段,已知被砍后每根小木棍的长度(被砍后长度小于50),试求原来木棍的最小可能长度 67 | 68 | ### 题目分析 69 | 70 | 首先要注意这道题要无视长度大于50的输入 71 | 72 | 对于这个问题,其实类似上面的问题,我们很自然的想到搜索,本题与上一题的区别在于,这道题目他没有告诉你原来有多少个木棍,而上一道题明确了原来有四个,那么这道题我们就可以枚举答案,如果我们知道最小的长度,那么我们已知总数,自然可以算出原来一共有多少个,这样以后,我们就将新的问题,转化成为已知的问题了。 73 | 74 | 当然,我们在枚举答案的时候,很显然,下限应该是已经有的木棍中的最小值 75 | 76 | 然后,这个问题的难点就在于如何剪枝。 77 | 78 | 首先,我们肯定是先放长的木棍,再放短的木棍,这样可以变得更加容易凑成 79 | 80 | 其次,我们当填充好一个位置的时候,就从后面查找缺口补上,如果补不上说明这条路径失败,就停止搜索 81 | 82 | ## 代码 83 | 84 | ```c++ 85 | #include 86 | #include 87 | const int N = 70 ; 88 | int n , cnt , tot , maxn , minn , tm[ N ] /* 2 */ ; 89 | void dfs( int res , int sum , int target , int p ) { 90 | if( res == 0 ) { 91 | printf("%d", target ); 92 | exit( 0 ); 93 | } 94 | if( sum == target ) { 95 | dfs( res - 1 , 0 , target , maxn ); 96 | return; 97 | } 98 | for( int i = p ; i >= minn ; i -- ) { // 2 3 99 | if( tm[ i ] && i + sum <= target ) { 100 | tm[ i ] -- ; 101 | dfs( res , sum + i , target , i ); 102 | tm[ i ] ++ ; 103 | if ( sum == 0 || sum + i == target ) //4 104 | break; 105 | } 106 | } 107 | return; 108 | } 109 | int main() { 110 | scanf("%d" , &n ) ; 111 | minn = N ; 112 | int temp; 113 | while( n -- ) { 114 | scanf("%d" , &temp ); 115 | if( temp <= 50 ) { 116 | cnt ++; 117 | tm[ temp ] ++; 118 | tot += temp; 119 | maxn = maxn > temp ? maxn : temp ; //1 120 | minn = minn < temp ? minn : temp ; 121 | } 122 | } 123 | temp = tot >> 1; 124 | for( int i = maxn ; i <= temp ; i ++ ) { 125 | if( tot % i == 0 ) { 126 | dfs( tot / i , 0 , i , maxn ); 127 | } 128 | } 129 | printf("%d" , tot ); 130 | return 0; 131 | } 132 | ``` 133 | 134 | ## 生日蛋糕 135 | 136 | [P1731[NOI1999]](https://www.luogu.org/problemnew/show/P1731) 137 | 138 | ### 题目大意: 139 | 140 | 一个蛋糕,每一层都是圆柱体,已知整个蛋糕的体积为Nπ,(n<=20000),并已知层数M,要求一种方案,使得表面积最小。 141 | 142 | ### 题目分析 143 | 144 | - 首先确定范围,已知体积,那么半径和高度不会超过28 145 | - 然后就可以进行搜索了。但是爆搜基本上就是会超时,所以我们需要进行剪枝 146 | - 当搜索到的蛋糕已经超过体积,或者表面积已经超过最优值,就停止搜索即可。 147 | - 如果当前的体积加上之后的体积 不到N的话,也直接 停止 148 | 149 | 150 | ## 代码 151 | 152 | ```c++ 153 | #include 154 | #include 155 | #include 156 | #include 157 | using namespace std; 158 | int r[30],h[30],minn=2147483647,n,m; 159 | void dfs(int x,int y,int k,int z) 160 | { if(y<0) return; 161 | if(x>m+1) return; 162 | if(k>=minn) return; 163 | if(y==0&&x==m+1) 164 | { k+=r[1]*r[1]; 165 | if(kminn) return; 169 | if(y-(r[x-1])*(r[x-1])*(h[x-1])*z>0) return; 170 | for(int i=r[x-1]-1;i>=z;i--) 171 | for(int j=h[x-1]-1;j>=z;j--) 172 | { 173 | if(y-i*i*j>=0&&x+1<=m+1) 174 | { r[x]=i; 175 | h[x]=j; 176 | dfs(x+1,y-i*i*j,k+(i*2*j),z-1); 177 | h[x]=0; 178 | r[x]=0; 179 | } 180 | } 181 | } 182 | int main() 183 | { 184 | scanf("%d%d",&n,&m); 185 | r[0]=(int)sqrt(n); 186 | h[0]=(int)sqrt(n); 187 | dfs(1,n,0,m); 188 | if(minn==2147483647) printf("%d",0); 189 | else printf("%d",minn); 190 | return 0; 191 | } 192 | ``` 193 | 194 | 195 | 196 | 197 | ## 吃奶酪 198 | 199 | [洛谷P1433](https://www.luogu.org/problemnew/show/P1433) 200 | 201 | ### 题目大意 202 | 203 | > 给定n块奶酪的坐标,求从(0,0)点出发,问最少要走多少距离 204 | 205 | ### 题目分析 206 | 207 | 我们每次一定是从一块奶酪走到另一块奶酪,走直线的近距离是最短的,所以,我们暴力枚举每一块奶酪访问的顺序,然后将答案相加,这样的话复杂度应该是n!,n=15时就太大了。 208 | 209 | 于是我们还要进行剪枝,如果当前路径已经大于求得的答案就可以退出。 210 | 211 | ```c++ 212 | #include 213 | #include 214 | #include 215 | using namespace std; 216 | int n,v[1001]; 217 | double x[100]; 218 | double y[20]; 219 | double dis[1001][1001]; 220 | double ans=1231234424.0; 221 | void dfs(int step,int now,double length) 222 | if(length>ans) return; 223 | if(step==n) 224 | { 225 | ans=min(ans,length); 226 | return; 227 | } 228 | for(int i=1;i<=n;i++) 229 | if(!v[i]) 230 | { 231 | v[i]=1; 232 | dfs(step+1,i,length+dis[now][i]); 233 | v[i]=0; 234 | } 235 | } 236 | int main() 237 | { 238 | cin>>n; 239 | for(int i=1;i<=n;i++) 240 | cin>>x[i]>>y[i]; 241 | x[0]=0;y[0]=0; 242 | for(int i=0;i<=n;i++) 243 | for(int j=0;j<=n;j++) 244 | dis[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); 245 | dfs(0,0,0.0); 246 | printf("%.2f",ans); 247 | return 0; 248 | } 249 | ``` 250 | 251 | ## 滑雪 252 | [P1434 [SHOI2002]](https://www.luogu.org/problemnew/show/P1434) 253 | 254 | ### 题目大意 255 | 256 | 给定一个二维数组,从某一个点出发,可以向四周滑落,但要求高度是减小的,问最长的滑坡的长度 257 | 258 | ### 题目分析 259 | 260 | 这个题目是一个明显的记忆化,因为每个点只能从高处滑下来,我们枚举起点,然后从起点开始往下搜索,搜索这个点所能往上走的最高距离,并把这个值保存下来,然后在后面再次搜索到这个值的时候,我们就可以用之前储存过的结果,降低复杂度拉。 261 | 262 | ### 代码 263 | 264 | ```c++ 265 | #include 266 | #define r(i,a,b) for (int i=a;i<=b;i++) 267 | #define pk putchar(32) 268 | #define ph putchar(10) 269 | using namespace std;int t,n,m; 270 | int h[101][101],ans,f[101][101] 271 | int max(int x,int y){return x>y?x:y;}int min(int x,int y){return x'9') if (c=='-') d=-1;a=a*10+c-48; 277 | while (c=getchar(),c>='0'&&c<='9') a=a*10+c-48; 278 | a*=d; 279 | } 280 | void write(int x){ 281 | if (x<0) {x=-x;putchar(45);} 282 | if (x) 283 | write(x/10); 284 | else return; 285 | putchar(x%10+48); 286 | } 287 | int dfs(int x,int y){ 288 | if (f[x][y]) return f[x][y]; 289 | int t=1; 290 | r(i,0,3) 291 | { 292 | int nx=x+dx[i]; 293 | int ny=y+dy[i]; 294 | if (nx>=1&&nx<=n&&ny>=1&&ny<=m&&h[x][y] 335 | #include 336 | #include 337 | using namespace std; 338 | #define inf 0x7fffffff 339 | int fx[4] = {-1, 0, 1, 0}; 340 | int fy[4] = {0, -1, 0, 1}; 341 | int f[110][110]; 342 | int mp[110][110]; 343 | int m, n, ans = inf; 344 | void dfs(int x, int y, int sum, bool frog){ 345 | if(x < 1 || y < 1 || x > m || y > m) return; 346 | if(mp[x][y] == 0) return; 347 | if(sum >= f[x][y]) return; 348 | 349 | f[x][y] = sum; 350 | if(x==m && y==m) 351 | if(sum < ans) ans = sum; 352 | return; 353 | } 354 | for(int i = 0; i < 4; ++i) 355 | { 356 | int xx = x + fx[i]; 357 | int yy = y + fy[i]; 358 | if(mp[xx][yy]) 359 | { 360 | if(mp[xx][yy] == mp[x][y]) 361 | dfs(xx, yy, sum, false); 362 | else dfs(xx, yy, sum+1, false); 363 | } else 364 | if(!frog) 365 | { 366 | mp[xx][yy] = mp[x][y]; 367 | dfs(xx, yy, sum+2, true); 368 | mp[xx][yy] = 0; 369 | } 370 | } 371 | } 372 | int main(){ 373 | memset(f, 0x7f, sizeof(f)); 374 | scanf("%d %d", &m, &n); 375 | for(int i = 1; i <= n; ++i){ 376 | int x, y, c; 377 | scanf("%d %d %d", &x, &y, &c); 378 | mp[x][y] = c + 1; 379 | 380 | } 381 | dfs(1, 1, 0, false); 382 | printf("%d", ans==inf ? -1 : ans); 383 | return 0; 384 | } 385 | ``` 386 | -------------------------------------------------------------------------------- /19-numTheory/质数筛专题.md: -------------------------------------------------------------------------------- 1 | # 质数筛专题 2 | 3 | ## A % B Problem 4 | 5 | ### 题目大意 6 | 7 | 区间质数个数 8 | 9 | 测试网站 10 | 11 | [luogu1865](https://www.luogu.org/problemnew/show/P1865#sub) 12 | 13 | ### 题目分析 14 | 15 | 题目要求我们找给定范围内的素数个数,而题目中m的范围非常大,显然求解质数时使用欧拉筛法效率最高。 16 | 17 | 利用 `while()` 循环遍历每一次的询问,再使用欧拉筛法求解`[2, r]`以及`[2, l]`之间的质数个数,然后相减。(注意范围左侧`l`要减去`1`之后求质数个数,因为如果`l`是质数时,所求范围质数包括`l`),此时时间复杂度为`n * m`。欧拉筛法返回的值是`[2, n]`之间的质数个数。 18 | 19 | ```c++ 20 | while(n--){ // 遍历询问次数 21 | cin>>l>>r; //输入左右区间 22 | if(l或r∉[1,m]) cout << "Crossing the line" << endl; //越界 23 | if(l=1) cout << Euler_prime(r) << endl; //质数筛法从2开始筛 24 | else cout << Euler_prime(r)-Euler_prime(l-1) << endl; //两侧质数个数相减 25 | } 26 | ``` 27 | 28 | 提交至测试网站,可以发现有部分数据超时。 29 | 30 | ![TLE](images/TLE.png) 31 | 32 | 33 | 可以发现,在处理每组数据时都使用欧拉筛法求解数组个数,有大量的数据被重复求解。 34 | 35 | 我们是否可以只使用一次欧拉筛法? 36 | 37 | 求解出最大范围即`[2, m]`之间的素数个数,并在使用欧拉筛法遍历时记录下来当前范围内的素数个数,并保存到数组中。 38 | 39 | 具体来说,定义数组`sum[maxn]`, 在`int Euler_prime(int k)`函数中: 40 | 41 | 42 | ```c++ 43 | for (int i = 2; i <= k; i++){ //外层循环从2~n开始遍历 44 | if (i是素数){ 45 | prime[tot++] = i; //当前状态是true,记录 46 | sum[i] = sum[i-1] + 1; //素数个数加1 47 | } else sum[i] = sum[i-1]; //素数个数不变 48 | 49 | ``` 50 | 51 | 在主函数中,如果`l`和`r`没有越界,直接输出 `sum[r] - sum[l-1]`。 52 | 53 | 54 | ### 代码示例 55 | 56 | ```c++ 57 | #include 58 | #include 59 | #include 60 | using namespace std; 61 | const int maxn=1e6+1; 62 | bool IsPrime[maxn]; 63 | int prime[maxn];//记录素数 64 | int sum[maxn]; //sum[j]记录2-j之间的素数个数 65 | 66 | int Euler_prime(int k){ 67 | int tot = 0; 68 | memset(IsPrime,true,sizeof(IsPrime)); //初始化,默认所有数为质数 69 | for (int i = 2;i <= k;i++){ //外层循环从2~n开始遍历 70 | if (IsPrime[i]) { 71 | prime[tot++] = i; //当前状态是true,记录 72 | sum[i]=sum[i-1]+1; //素数个数加1 73 | } else sum[i]=sum[i-1]; //素数个数不变 74 | for (int j = 0;j < tot;j++){ //遍历素数表 75 | if (i*prime[j] > k) break; //超过最大范围,跳出 76 | IsPrime[i*prime[j]] = false;//将倍数i倍*某个质数筛除 77 | if (i%prime[j] == 0) break;//保证只筛到以prime[j]为最小质因数的数,退出内层循环 78 | } 79 | } 80 | } 81 | 82 | int main(){ 83 | int l, r, n, m; 84 | cin >> n >> m; //询问次数,范围 85 | Euler_prime(m); 86 | while(n--){ //遍历询问次数 87 | cin >> l >> r; //输入左右区间 88 | if (l<1 || l>m || r<1 || r>m) cout<<"Crossing the line"< 120 | using namespace std; 121 | const int N = 44444; 122 | 123 | bool is_prime[N]; 124 | int prime[N], prime_cnt, phi[N]; 125 | 126 | void getPrime(int N){ 127 | memset(is_prime, true, sizeof is_prime); 128 | is_prime[1] = false; 129 | phi[1] = 1; 130 | for (int i =2; i < N; i++){ 131 | if (is_prime[i]){ 132 | prime[++prime_cnt] = i; 133 | phi[i] = i-1; 134 | } 135 | for (int j = 1; j <= prime_cnt && prime[j]*i < N; j++){ 136 | is_prime[prime[j]*i] = false; 137 | if (i % prime[j] == 0){ 138 | phi[i*prime[j]] = phi[i] * prime[j]; 139 | }else{ 140 | phi[i*prime[j]] = phi[i] * phi[prime[j]]; 141 | } 142 | } 143 | } 144 | } 145 | 146 | int main() { 147 | int n, ans; 148 | cin >> n; 149 | if (n == 1){ 150 | ans = 0; 151 | }else{ 152 | getPrime(n+7); 153 | ans = 0; 154 | for (int i = 1; i < n; i++){ 155 | ans += phi[i]; 156 | printf("%d %d\n", i, phi[i]); 157 | } 158 | ans = ans * 2 + 1; 159 | } 160 | cout << ans << endl; 161 | return 0; 162 | } 163 | 164 | ``` 165 | 166 | ## 【模板】线性筛素数 167 | 168 | [洛谷P3383](https://www.luogu.org/problemnew/show/P3383) 169 | 170 | ### 题目大意 171 | 172 | 给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) 173 | 174 | ### 题目分析 175 | 176 | 这个题目主要就是一个筛法的模板题,因为给定的N非常大,所以这个题目应该使用线性筛 177 | 178 | ### 代码示例 179 | 180 | ```c++ 181 | #include 182 | using namespace std; 183 | int n,m,p[10000005]; 184 | vectorpls; 185 | void get_pls(){ 186 | for(int i=2;i<=n;i++){ 187 | if(!p[i])pls.push_back(i); 188 | int si=pls.size(); 189 | for(int j=0;j 228 | using namespace std; 229 | int n,s=0; 230 | bool p(int n){ 231 | for (int i=2;i<=int(sqrt(n));i++) 232 | if (n%i==0) return false; 233 | return true; 234 | }//判断是否是质数的布尔型函数 235 | 236 | int main(){ 237 | int i,j; 238 | cin>>n;//读入 239 | for (i=4;i<=n;i++)//从4到n扫 240 | for (j=2;j<=i/2;j++)//因为1不是质数,不用考虑,一直到i的一半就可以了,不然i一减就重复了 241 | if (i%2!=0) break;//因为哥德巴赫猜想是说大于4的偶数,如果i是奇数就弹了 242 | else if (p(j)&&p(i-j)) {cout< 265 | #include 266 | #include 267 | #include 268 | #include 269 | #include 270 | #include 271 | #include 272 | #define INF 0x3f3f3f3f 273 | #define ms(x,y) memset(x,y,sizeof(x)) 274 | using namespace std; 275 | 276 | typedef long long ll; 277 | 278 | const double pi = acos(-1.0); 279 | const double eps = 1e-8; 280 | const int maxn = 1e6 + 10; 281 | 282 | ll a, b; 283 | int num_prime, num_prime2; 284 | bool not_prime[maxn]; 285 | int prime[maxn]; 286 | ll prime2[maxn]; 287 | 288 | void get_prime() //埃氏筛法 289 | { 290 | ms(not_prime, 0); 291 | not_prime[1] = 1; 292 | num_prime = 0; 293 | for (ll i = 2; i < maxn; i++) 294 | { 295 | if (!not_prime[i]) 296 | { 297 | prime[++num_prime] = i; 298 | for (ll j = 2 * i; j < maxn; j += i) 299 | not_prime[j] = 1; 300 | } 301 | } 302 | } 303 | 304 | void get_prime_ab() //用得到的素数去筛选a~b 305 | { 306 | ms(not_prime, 0); 307 | for (int i = 1; i <= num_prime; i++) 308 | { 309 | ll p = a / prime[i]; 310 | if (p <= 1) p = 2; 311 | for (ll j = prime[i] * p; j <= b; j += prime[i]) 312 | { 313 | if (j >= a) not_prime[j - a] = 1; 314 | } 315 | } 316 | if (a == 1) not_prime[0] = 1; 317 | num_prime2 = 0; 318 | for (int i = 0; i <= b - a; i++){ 319 | if (!not_prime[i]){ 320 | prime2[++num_prime2] = i + a; 321 | } 322 | } 323 | if (num_prime2 <= 1) printf("There are no adjacent primes.\n"); 324 | else{ 325 | ll min = INF, max = -INF, minl, minr, maxl, maxr; 326 | for (int i = 1; i < num_prime2; i++){ 327 | if (prime2[i + 1] - prime2[i] > max){ 328 | max = prime2[i + 1] - prime2[i]; 329 | maxl = prime2[i]; 330 | maxr = prime2[i + 1]; 331 | } 332 | if (prime2[i + 1] - prime2[i] < min){ 333 | min = prime2[i + 1] - prime2[i]; 334 | minl = prime2[i]; 335 | minr = prime2[i + 1]; 336 | } 337 | } 338 | printf("%lld,%lld are closest, %lld,%lld are most distant.\n", minl, minr, maxl, maxr); 339 | } 340 | } 341 | 342 | int main(){ 343 | get_prime(); 344 | while (~scanf("%lld%lld", &a, &b)){ 345 | get_prime_ab(); 346 | } 347 | return 0; 348 | } 349 | ``` 350 | 351 | ## Sum 352 | 353 | [ACM-ICPC 2018 南京赛区网络预赛 J](https://nanti.jisuanke.com/t/A1956) 354 | 355 | ### 题目大意 356 | 357 | f(n) 表示 n =a*b (a,b 都为非 平方倍数) ,a,b不同时的对数 358 | 计算 f(1)+f(2)+…+f(n) 359 | 360 | ### 题目分析 361 | 362 | 题目分析:我们分析一下在和当中每个数的贡献度 363 | 举例 当 n = 8时 364 | 因子=1时的 贡献度 有 1\*1 (1\*2 ,1\*3, 1\*5,1\*6,1\*7 ) 后面这一部分要\*2 1+5\*2=11 365 | 因子=2时的贡献度 有 2\*2 (2\*3) (因为2\*1可以在之前算到,所以我们再后面计算的时候只从比当前的数大的对数)1+1\*2=3 366 | 367 | \>=3时 就没有贡献 368 | 所以总的贡献就是3+11=14 369 | 总的来说,我们只要算出到n为止有多少个满足条件的a,b,那么对于每一个非平方数倍数因子i ,他的贡献度就是 (cnt[n/i]-cnt[i])\*2+1 370 | 那么我们只要预处理用筛法筛去平方数和他们的倍数即可 371 | 372 | ### 代码示例 373 | 374 | ```c++ 375 | #include 376 | using namespace std; 377 | typedef long long ll; 378 | const int maxn=2e7+50; 379 | ll vis[maxn+5]; 380 | ll cnt[maxn+5]; 381 | void init() 382 | { 383 | for(int i=0; i<=maxn; i++) vis[i]=1; 384 | for(int i=2; i*i<=maxn; i++) 385 | { 386 | int k=i*i; 387 | if(!vis[k])continue; 388 | for(int j=k; j<=maxn; j+=k) 389 | { 390 | vis[j]=0; 391 | } 392 | } 393 | for(int i=1; i<=maxn; i++) 394 | { 395 | cnt[i]=cnt[i-1]+vis[i]; 396 | } 397 | } 398 | int main() 399 | { 400 | init(); 401 | int T; 402 | scanf("%d",&T); 403 | while(T--) 404 | { 405 | int n; 406 | ll ans=0; 407 | scanf("%d",&n); 408 | for(int i=1; i*i<=n; i++) 409 | { 410 | if(!vis[i])continue; 411 | ans+=((cnt[n/i]-cnt[i])*2+1); 412 | //cout<