Event summary
Topic aspect
- I have strictly controlled the whole problem setting of this competition. After the ABA problem setting group, the ABC problem setting group's first battle
- One obvious aspect is that the title has become more beautiful (alphanumeric and subscript are all used) l a t e x latex latex, code blocks are all in the form of backquotes)
- The title style is closer to the ladder style
- The quality of the topic is also high. I cut off all idea s that are not in line with the style of the ladder
- After three tests, the correctness is guaranteed
Problem group
A B A & A B ABA\&AB The ABA & AB team retired, A B C ABC Upper level of ABC problem group
Team leader: Bei
Team members: Xun, Jie, Chi, Ju
Difficulty control
- By beautifying the problem surface, repeatedly improving and modifying the meaning of the problem to facilitate everyone's understanding, and giving tips, we can continuously reduce the difficulty and achieve an appropriate difficulty
- It was found that there were still yards in the whole game, so the 4h game time was given
- L2-3 tested the examiners, and the three examiners couldn't Therefore, a prompt is given: convert multi-source and multi sink points to single source and single sink points
- L3-1 gives the hint of complete binary tree, because a top-level full score test question taker of PTA who does not want to be named has forgotten the definition of complete binary tree, and it is unexpected that many people must have confused the definitions of complete binary tree and perfect binary tree
- L3-3 was originally a 5-point delivery proposition that can only be written for 70 minutes at the first level of school selection. I replaced it with a topic that is easy to cheat points and may be able to be done by someone
Degree of simulation
- It can be said that it is the closest school selection to the official competition, whether it is topic style, topic difficulty or competition mode
- In the mode, you can only view your own submission records (you can't see whether others submit WA or MLE), but you can see the ranking and the specific scores of other players (you can see the list in the official competition, and you can refer to the answers of your teammates)
L1 basic level
L1-1 most delicious
Author: Bei
Algorithm idea
- It continues the guessing style of the first question in JMU's school selection over the years. In previous years, this sub question was given in English and there was escape source code. This time, it was given in Chinese
- There are two kinds of people. The one who knows the best food is mine. It will pass in 10 seconds
- If you don't know, follow the last Hint of Hint and follow the steps said by Hint. It's too late to write a code
- I have been controlling the difficulty and direction of the whole problem group. I hope you can still get more points and the degree of distinction will be more obvious.
code implementation
#include <bits/stdc++.h> using namespace std; int main() { string L="GNINIEBNIL"; for(int i = 0 ; i < L.size() ;i ++) { L[i]= L[i] - 'A' + 'a'; } reverse(L.begin(),L.end()) ; cout << L <<" shi JMU zui cai de ren."; }
L1-2 repeater
Author: Bei
thinking
- Send sub questions properly. There's nothing to say
code implementation
#include <bits/stdc++.h> using namespace std; #define el '\n' #define rep(i, a, b) for (int i = (a); i <= (b); i++) int main() { cin.tie(0); cout.tie(0); rep(i, 1, 5) cout << "beibei shi Jimei University zui cai de ren" << el; }
L1-3 JMU Cangjie reproduction -- what type of Chinese characters of Jue Jue Zi
Author: Xun
meaning of the title
The last sentence is the key, that is, output n lines "-"
Problem solving ideas
Output n lines' - '
Chinese Chinese characters make complaints about the "add, subtract, multiply and divide" of the Chinese characters, but not half the dash.
It's really the father who dug his son's grave - digging his son
code
#include<bits/stdc++.h> using namespace std; int main() { int n; cin >> n; for (int i = 0; i < n; i++) { if (i)cout << endl; cout << "—"; } }
L1-4
Author: Jie
Algorithm idea
The exchanged bottles can also be exchanged repeatedly, and the circular structure can simulate the exchange process until they can no longer be exchanged
code implementation
#include<bits/stdc++.h> using namespace std; int n, k, m; int main() { cin >> n >> k; int ans = 0; do { ans += n; m += n; //Get n caps n = m / k; m %= k; }while(n); cout << ans; }
L1-5 reverse repeater
Author: Bei
Topic report
- I'm afraid someone has been stuck in the blank space at the end of the line and so on. I'm still doing the problem. Finally, I specially reminded
- I set up six test points
- Test point 0, basically correct, small data, 3 points
- Test point 1, one sentence only, 1 point
- Test point 2, three sentences are the same, 1 point
- Test point 3, two sentences, 1 point
- Test point 4, big data n = 1 e 5 n=1e5 n=1e5, speech length is 100 characters, 8 points
- Test point 5, big data n = 1 e 5 n=1e5 n=1e5, if all speeches are the same, 1 point
Algorithm idea
- We output the previous sentence until we encounter different speeches
- Then let's open a cnt to count how many times the previous sentence appeared, and then consider whether he needs to redo it
- But in this way, we must have one more time left in the end, so we can output it once in the end
code implementation
#pragma GCC optimize("O3") #include <bits/stdc++.h> #include <unordered_map> #include <unordered_set> using namespace std; #define debug(x) cerr << #x << ": " << x << '\n' #define bd cerr << "----------------------" << el #define el '\n' #define cl putchar('\n') #define pb push_back #define eb emplace_back #define x first #define y second #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define loop(i, a, b) for (int i = (a); i < (b); i++) #define dwn(i, a, b) for (int i = (a); i >= (b); i--) #define ceil(a, b) (a + (b - 1)) / b #define ms(a, x) memset(a, x, sizeof(a)) #define inf 0x3f3f3f3f #define db double typedef long long LL; typedef long double LD; typedef pair<int, int> PII; typedef pair<db, db> PDD; typedef vector<int> vci; typedef map<int, int> mii; typedef mii::iterator mii_it; const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7; const double PI = acos(-1), eps = 1e-8; int T, n, m, cnt; string last, now; void out() { if (cnt < 3) { rep(i, 1, cnt) { cout << last << el; } } else cout << last << el; return ; } int main() { // freopen("2.in", "r", stdin); // freopen("2.out", "w", stdout); cin.tie(0); cout.tie(0); cin >> n; getchar(); rep(i, 1, n) { getline(cin, now); if (now != last) { if(cnt) out(); cnt = 1; last = now; } else { cnt++; } } out(); }
L1-6 different strings
Author: Chi
Topic statement
General idea: give n strings for each group of test data, and ask how many different strings there are in total, which is not case sensitive
Problem solving ideas
-
Read the whole line into the string first. Because there are spaces in the string, you can't directly read cin and scanf. It is recommended to read the whole line with getline(cin,s)
-
Traverses the entire string, changing lowercase letters to uppercase and unifying the case of letters. Do not remove spaces, because AA and AA are two different strings.
-
The first method is to use set, directly store the string in set, and finally output the number in set (set has the function of automatic de duplication).
If you can't use set, you can build a string array and compare each newly entered string with each bit in the array. If it's different, put the new string into the array, and then add 1 to the number of different strings.
code implementation
//Method 1: use set #include<bits/stdc++.h> using namespace std; #define ll long long typedef pair<int, int> PLL; #define inf 0x3f3f3f3f int main(){ int t; cin>>t; while(t--){ int n; cin>>n; getchar(); set<string>s; for(int i=0;i<n;i++){ string a; getline(cin,a); int len=a.length(); for(int j=0;j<len;j++){ if(a[j]>='a'&&a[j]<='z')a[j]=a[j]-'a'+'A'; } s.insert(a); } cout<<s.size()<<endl; } }
//Method 2: #include<bits/stdc++.h> using namespace std; #define ll long long typedef pair<int, int> PLL; #define inf 0x3f3f3f3f string a[1100]; int tot = 0; int main() { int t; cin >> t; while (t--) { int n; cin >> n; getchar(); int cnt = 0; for (int k = 0; k < n; k++) { string s; getline(cin, s); int len = s.length(); for (int j = 0; j < len; j++) { if (s[j] >= 'a' && s[j] <= 'z')s[j] = s[j] - 'a' + 'A'; } int i; for (i = 0; i < cnt; i++) { int len2 = a[i].length(); if (len2 != len)continue; int j; for (j = 0; j < len; j++) { if (a[i][j] != s[j])break; } if (j == len)break; } if (i == cnt)a[cnt++] = s; } cout << cnt << endl; } }
L1-7 FJCC-GPLT
Author: Xun
Topic statement
Given the scores of 10 people from n teams, simulate according to the rules of the ladder competition, calculate the scores of each team and sort them.
Problem solving ideas
- Read the team name according to the line input
- For each team, input the scores of 10 people and 15 questions to process the scores of L1, L2 and L3 of the team
- Judge whether the l1 score is 800 points, add the l2 score if it is enough, and add the l3 score if the l1 score is 800 points and the l2 score is 400 points. Pay attention to whether the l3 score is valid and meet the l2 score first, so as to prevent the l2 score from being 400 points, but its score is invalid.
- Finally, sort the second keyword according to the team score as the first keyword and the team name dictionary order, and then output it.
code implementation
#include<bits/stdc++.h> #include<bits/stdc++.h> using namespace std; map<string, int> m; vector<pair<int, string>> p; int main() { int n; cin >> n; getchar(); for (int i = 0; i < n; i++) { string s; getline(cin, s); int l1 = 0, l2 = 0, l3 = 0; for (int l = 0; l < 10; l++) { int g; for (int j = 0; j < 15; j++) { cin >> g; if (j < 8)l1 += g; else if (j < 12)l2 += g; else l3 += g; } } int res = l1; if (l1 >= 800)res += l2; if (l1>=800&&l2 >= 400)res += l3; getchar(); p.push_back(make_pair(-res, s)); } sort(p.begin(), p.end()); for (auto k : p) { cout << k.second << ' ' << -k.first << endl; } }
L1-8 "who's lying"
Author: Bei
Topic report
-
Originally, I wanted to lose the accuracy of double and directly compare the total long long. The accuracy must be higher than the average value. After thinking about it, I still didn't deliberately card it
-
I set four test points for this question
-
No one really uses "LBN" and "LBN" a 0 a_0 a0} corresponding string?
-
Test point 1, basically correct, 4 points
-
Test point 2, maximum data, all correct, open long long, 10 points
-
Test point 3 is basically correct, but it will be stuck by "LBN" and "LBN", 5 points
-
Test point 4, test nobody, 1 point
-
because 1 0 5 × 1 0 5 = 1 0 10 10^5 \times 10^5 =10^{10} one hundred and five × 105 = 1010, so you need to turn on LL
Algorithm idea
- It's easy to use a map. Just record the values of two maps. The map has its own sorting. Remember to turn on LL for seconds
- It's OK to write your own hash. A string can be up to 5 digits in length, and you can regard it as hexadecimal
code implementation
#pragma GCC optimize("O3") #include <bits/stdc++.h> #include <unordered_map> #include <unordered_set> using namespace std; #define debug(x) cerr << #x << ": " << x << '\n' #define bd cerr << "----------------------" << el #define el '\n' #define cl putchar('\n') #define pb push_back #define eb emplace_back #define x first #define y second #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define loop(i, a, b) for (int i = (a); i < (b); i++) #define dwn(i, a, b) for (int i = (a); i >= (b); i--) #define ceil(a, b) (a + (b - 1)) / b #define ms(a, x) memset(a, x, sizeof(a)) #define inf 0x3f3f3f3f #define db double typedef long long LL; typedef long double LD; typedef pair<int, int> PII; typedef pair<db, db> PDD; typedef vector<int> vci; typedef map<int, int> mii; typedef mii::iterator mii_it; const int N = 110, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7; const double PI = acos(-1), eps = 1e-8; int T, n, m; map<string , LL> last, now; string name[N]; string b = "1", s;//Because S is composed of letters, using a numeric string to represent Beibei can avoid hash conflict //State design in hash LL x; int main() { // freopen("2.in", "r", stdin); // freopen("2.out", "w", stdout); cin.tie(0); cout.tie(0); cin >> n >> m; rep(i, 1, m) { cin >> x; last[b] += x; } rep(i, 1, n) { cin >> s; name[i] = s; rep(i, 1, m) { cin >> x; last[s] += x; } debug(last[s]); } cin >> now[b]; int cnt = 0; rep(i, 1, n) cin >> now[name[i]]; for (auto it : last) { if(it.y >= last[b] && now[it.x] < now[b]) {//The historical average is higher than Beibei, and the score is lower than Beibei cnt ++ ; cout << it.x << el; } } if(!cnt) { cout << "Yeah! Nobody!" <<el; } }
L2 progressive class
L2-1 tree height
Author: Ju
meaning of the title
To build a tree, find the height of the tree (the number of layers of the tree)
Problem solving ideas
After building the tree according to the meaning of the topic, dfs just click.
code
#include<bits/stdc++.h> using namespace std; const int maxn = 1e5 + 10; vector<int> G[maxn]; int ans = 0; // The root node depth is 1 void dfs(int cur, int deep) { ans = max(ans, deep); for (auto& val : G[cur]) { dfs(val, deep + 1); } } // I starts from 0, and a[i] represents the father number of i+1 int main() { ios::sync_with_stdio(false); int n; cin >> n; int i; int num; for (i = 1; i < n; i++) { cin >> num; G[num].push_back(i); } dfs(0, 1); cout << ans << endl; return 0; }
L2-2 ACM membrane culture
Author: Bei
Topic report
To investigate the application of set and string, I set seven test points
- Test point 0. With alias, basically correct, 5 points
- Test point 1. Case sensitive alias, small data, 1 point
- Test point 2. Case sensitive alias, small data, 1 point
- Test point 3. No alias, basically correct, 4 points
- Test point 4. n is the largest, the line length is the longest, and the character is the longest. It has alias case, 9 points
- Test point 5. Shen should be judged as Normal according to the meaning of the question, the case of card Shen, and the case of Shen and Shen. This person did not appear in the worship behavior, but it was judged as Dalao. Only those who appeared were recorded and will be stuck, 3 points
- Test point 6. For example, if JieShen's subsequent record is Jie, he will be stuck, 2 points
Algorithm idea
- In fact, the four string grading is a cover. You only need numbers to represent the grade
- When you encounter this kind of simulation problem, as long as you think carefully, you can save a lot of detours. If you hit it directly, it's easy to hit more and more bug s
- In fact, this question is not difficult. Originally, the question I planned to give also included that everyone has multiple aliases, and the level level will change according to the aliases
- Considering the difficulty of the whole copy, I'm afraid it's too difficult, so I'll reduce it to the final version
- The four sets < string > maintain the alias pool, representing the level, which has little to do with his original name
- First use the find function of string to find the space, substr divides this sentence into subject predicate table, and then use the find and substr functions of string to find the keyword
code implementation
#pragma GCC optimize("O3") #include <bits/stdc++.h> #include <unordered_map> #include <unordered_set> using namespace std; #define debug(x) cerr << #x << ": " << x << '\n' #define bd cerr << "----------------------" << el #define el '\n' #define cl putchar('\n') #define pb push_back #define eb emplace_back #define x first #define y second #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define loop(i, a, b) for (int i = (a); i < (b); i++) #define dwn(i, a, b) for (int i = (a); i >= (b); i--) #define ceil(a, b) (a + (b - 1)) / b #define ms(a, x) memset(a, x, sizeof(a)) #define inf 0x3f3f3f3f #define db double typedef long long LL; typedef long double LD; typedef pair<int, int> PII; typedef pair<db, db> PDD; typedef vector<int> vci; typedef map<int, int> mii; typedef mii::iterator mii_it; const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7; const double PI = acos(-1), eps = 1e-8; int T, n, m; string name, line, sub, ver, pred; int level1, level2, first_blank, second_blank; char c; set<string> L[5]; int get_level(string &s) { if (s == "ZhanShen") return 4; else if (s == "BeiShen") return 1; else if (s.size() > 4 && s.substr(s.size() - 4, 4) == "Shen") return 3; else { for(int i = 1; i <= 4; i ++ ) { if(L[i].find(s) != L[i].end())//Look in the alias pool { return i; } } return 2; } } int main() { cin.tie(0); cout.tie(0); cin >> n; getchar(); while (n--) { cin >> name >> c; getchar(); getline(cin, line); cout << name << " : " << line << el; first_blank = line.find(' '); sub = line.substr(0, first_blank);//subject second_blank = line.find(' ', first_blank + 1); ver = line.substr(first_blank + 1, second_blank - first_blank - 1);//predicate pred = line.substr(second_blank + 1, line.size() - 1 - second_blank);//Predicative cout <<"Translate : "; if (pred.find("low-level") != pred.npos || pred.find("stupid") != pred.npos || pred.find("fool") != pred.npos || pred.find("caigou") != pred.npos) { level1 = get_level(name); cout << name; switch(level1) { case 1:{ cout << " is ZiCao-ing now." << el; break; } case 2:{ cout << " is MaiRuo-ing now." << el; break; } case 3: case 4:{ cout << " is ZhuangCai-ing now." << el; break; } } if(sub != name && sub != "I") L[level1].insert(sub); } else if (pred.find("high-level") != pred.npos || pred.find("clever") != pred.npos || pred.find("genuis") != pred.npos || pred.find("talent") != pred.npos) { level1 = get_level(name); level2 = get_level(sub); cout << name; if (level1 < level2) { cout << " is truly worshipping now." << el; } else { cout << " worships for taking care of other people's feelings." <<el; } } else cout <<"sto "<< name << " orz." <<el; } }
L2-3 the dishes are home
Author: Bei
Topic report
summary
- This topic is a multi-source and multi sink shortest path problem. The essence is to investigate the single source and single sink shortest path problem
- The problem is still the shortest path. You only need to add two points on the template - super source point + Super sink point
- I also have roughly three grades:
- First gear: each time, directly select the fresh supermarket or deposit point as the starting point, and then run the shortest circuit for many times. The score is expected 10 − 16 branch 10-16 points 10 − 16 points. The two best cases are passing test points 1,2,5,6 (p is the starting point, 14 points) or test points 1,2,3,4,6 (q is the starting point, 16 points)
- Second gear: select the small and medium-sized P and Q as the starting point, and it is estimated that 18 − 19 18-19 18 − 19 points, passing the first five test points
- The third level, positive solution super source point + Super sink point, is expected to score 25 points, passing all test points.
Data analysis
- I set up seven test points
- Test point 1: n = 100 , m = 200 , p = q = 10 n=100,m=200,p=q=10 n=100,m=200,p=q=10, test basic correctness, 3 points
- Test point 2: n = 100 , m = 200 , p = q = 10 n=100,m=200,p=q=10 n=100,m=200,p=q=10, test the basic correctness, 3 points. These two test points can run multiple times, and the shortest path of single source must pass
- Test point 3: n = 1 e 5 , m = 2 e 5 , p = 5 e 4 , q = 10 n=1e5,m=2e5,p=5e4,q=10 n=1e5,m=2e5,p=5e4,q=10, 3 points
- Test point 4: n = 1 e 5 , m = 2 e 5 , p = 99990 , q = 10 n=1e5,m=2e5,p=99990,q=10 n=1e5,m=2e5,p=99990,q=10, only fresh supermarket and deposit point are tested, 1 point
- Test point 5: n = 1 e 5 , m = 2 e 5 , p = 10 , q = 5 e 4 n=1e5,m=2e5,p=10,q=5e4 n=1e5,m=2e5,p=10,q=5e4, 4 points
- Test point 6: test that all fresh food supermarkets and deposit points are not connected, 1 point
- Test point 7: n = 1 e 5 , m = 2 e 5 , p = 80 , q = 80 , w i ∈ [ 5 e 3 , 1 e 4 ] n=1e5,m=2e5,p=80,q=80,w_i \in[5e3,1e4] n=1e5,m=2e5,p=80,q=80,wi ∈ [5e3,1e4], 10 points
Algorithm idea promotion
Algorithm 1: violence + shortest path
- In the shortest path problem, the starting point and ending point can undoubtedly be changed
- An obviously violent idea, take each fresh supermarket as the starting point and run the shortest path once
- Then, in each shortest path, select the shortest distance to the deposit location and set it as the shortest distance of the current time
- So run a total of p p The answer can be obtained by the shortest path p times
- My code here is S P F A SPFA SPFA, of course D i j k s t r a Dijkstra Dijkstra writing is the same (after all, it is timeout)
Complexity analysis
- Time complexity. For the shortest path, the average case of the shortest path of SPFA is O ( k ∣ E ∣ ) O(k|E|) O(k∣E∣), k k k is a constant, but if the questioner deliberately cards you, it may be achieved O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(∣ V ∣ E ∣) (this is rare) if Dijkstra heap is used to optimize, the complexity is stable O ( V log E ) O(V \log E) O (vloge), Dijk heap optimization is recommended for ACM competition. The figure is O ( ∣ E ∣ ) O(|E|) O(∣ E ∣), scan the end point again after each SPFA run, which is O ( q ) O(q) O(q), the shortest circuit needs to run p p p times, the total time complexity is O ( p ( k ∣ E ∣ + q ) ) O(p(k|E|+q)) O(p(k∣E∣+q))
- Space complexity, defining a queue, dis,vis array, as O ( V ) O(V) O(V)
Algorithm 2: super source sink + shortest path
- In the above process, we will find that our S P F A SPFA During SPFA, many repeated paths will be run
- Doing these processes repeatedly not only takes time, but also space
- So what can we do to eliminate these processes? How to optimize has become the focus of this topic
- Let's assume that the graph is like this
- Green represents the fresh supermarket, red represents the deposit office, and black represents other places
- Here I'll first talk about a practice, and then explain why
- Suppose that all fresh supermarkets are the same wholesaler, is it not too much for us to install a "wormhole conveyor" between fresh supermarkets and fresh supermarkets?
- That is, edges with a cost of 0 can be established between two green nodes, and between two red nodes
- Why can this be done?
- To the contrary, if this is the shortest path we finally seek, then there are only black nodes on this path except for the starting point and the ending point
- As shown in the figure below, assuming that our answer is (1 – > 3), it is obvious that starting from 2 will be better
- That is, if there are non black nodes on the original path, the original shortest path must be replaced by a shorter path
- After the new graph is established, if the source point initially selected does not run the shortest path of the answer, we can spend "0" (unconditionally transfer to other green nodes) to run the new answer, so as to omit the repeated process of finding the shortest path
- In this way, we only need to build two edges (complete subgraph) in the same non black node, and finally run the shortest path once to calculate the answer
Re optimization
- We will find that we only need to ensure the connectivity between non black nodes of the same color and the same color, so we don't need to establish a complete subgraph, we just need to establish a tree, so the time to establish edges can be much less
Scheme I
- From the first node in P to each node after P, an edge with a weight of 0 is established, that is, "chrysanthemum graph"
Scheme II
- The two adjacent sides before and after the subscript in P establish a connecting edge with a weight of 0, that is, "linked list"“
Programme III
-
Define a super source point with the number of 0 (not used in the figure), and establish its edge connection with each number weight of 0 in P
-
Here we choose scheme 3. The above two are also feasible, but scheme 3 is easier to write and the amount of code is smaller
-
(the same is true for the implementation of red node Q)
Complexity analysis
- Time complexity. For the shortest path, the average case of the shortest path of SPFA is O ( k ∣ E ∣ ) O(k|E|) O(k∣E∣), k k k is a constant. Construction drawing is O ( ∣ E ∣ ) O(|E|) O(∣ E ∣), the connecting edge of the super source point and sink point is O ( p + q ) O(p+q) O(p+q), the total time complexity is O ( k ∣ E ∣ + p + q ) O(k|E|+p+q) O(k∣E∣+p+q)
- Space complexity, defining a queue, dis,vis array, as O ( V ) O(V) O(V)
code implementation
#pragma GCC optimize("O3") #include <bits/stdc++.h> #include <unordered_map> #include <unordered_set> using namespace std; #define debug(x) cerr << #x << ": " << x << '\n' #define bd cerr << "----------------------" << el #define el '\n' #define cl putchar('\n') #define pb push_back #define eb emplace_back #define x first #define y second #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define loop(i, a, b) for (int i = (a); i < (b); i++) #define dwn(i, a, b) for (int i = (a); i >= (b); i--) #define ceil(a, b) (a + (b - 1)) / b #define ms(a, x) memset(a, x, sizeof(a)) #define inf 0x3f3f3f3f #define db double typedef long long LL; typedef long double LD; typedef pair<int, int> PII; typedef pair<db, db> PDD; typedef vector<int> vci; typedef map<int, int> mii; typedef mii::iterator mii_it; const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7; const double PI = acos(-1), eps = 1e-8; int T, n, m, p, q; int x, y; vector<PII> v[N]; int SPFA() { queue<int> q; q.push(0); //Put in starting point bool vis[N]; //Tag, whether in queue int dis[N]; //Distance to starting point memset(dis, 0x3f, sizeof dis); //Initialization is infinite memset(vis, 0, sizeof vis); //The initial is not in the queue vis[0] = 1; //The starting point has joined the team dis[0] = 0; //The distance from the super source point to itself is 0 while (!q.empty()) { int u = q.front(); q.pop(); for (auto it : v[u]) { //Accessing u's neighbors through the adjacency table if (dis[u] + it.y < dis[it.x]) { //If you can relax the edges dis[it.x] = dis[u] + it.y; if (!vis[it.x]) { //If it is not in the queue, it is put in the queue vis[it.x] = 1; q.push(it.x); } } } } if (dis[n + 1] == dis[n + 2]) return -1; //If the graph is not connected return dis[n + 1]; } int main() { // freopen("6.in","r",stdin); // freopen("6.out","w",stdout); cin.tie(0); cout.tie(0); cin >> n >> m >> p >> q; int u; rep(i, 1, p) { cin >> u; v[u].pb({0, 0}); v[0].pb({u, 0}); } rep(i, 1, q) { cin >> u; v[u].pb({n + 1, 0}); v[n + 1].pb({u, 0}); } int a, b, c; rep(i, 1, m) { cin >> a >> b >> c; v[a].pb({b, c}); v[b].pb({a, c}); } cout << SPFA() << el; }
L2-4 you just got home
Author: Xun
Topic statement
Concise and comprehensive, give an undirected graph and find the minimum dictionary order M S T MST MST (minimum spanning tree)
Problem solving ideas
- Thinking about classics M S T MST Greedy algorithm of MST—— K r u s k a l Kruskal Kruskal, after sorting by edge weight, select the edge with the smallest weight and will not form a ring
- For edges with different weights, only the smaller one can ensure the minimum spanning tree, without considering the name of the edge. For several edges with the same weight, if there is a "conflict", that is, these edges can only select some of them, then obviously we need to select the edges with the smallest edge name list
- Therefore, when sorting edges, the edge weight is the first keyword, and the edge name dictionary order is the second keyword k r u s k a l kruskal kruskal algorithm is used to select edges, and finally judge whether there is a spanning tree.
- As for why each selection of the side with the smallest name dictionary order can minimize the dictionary order of the last sorted and spliced string, readers can prove it by themselves and refer to scheme a and b s o r t ( a ) ≤ s o r t ( b ) sort(a)≤sort(b) sort(a)≤sort(b), c ≤ d c≤d c ≤ d is not hard to get s o r t ( a + c ) ≤ s o r t ( b + d ) sort(a+c)≤sort(b+d) sort(a+c) ≤ sort(b+d), note s o r t ( ) sort() sort() is the operation of splicing after sorting the edge names in the group in the meaning of the question. Therefore, this method can gradually construct a dictionary with the smallest order M S T MST MST
code implementation
#include<map> #include<stdio.h> #include<iostream> #include<vector> #include<string> #include<math.h> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define ll long long struct edge { string name; int u, v,cost; }e[200005]; int fa[100005]; int find(int r) { return fa[r] == r ? r : fa[r] = find(fa[r]); } void join(int x, int y) { int fx = find(x), fy = find(y); fa[x] = fy; } bool cmp(edge a, edge b) { return a.cost < b.cost || (a.cost == b.cost&&a.name < b.name); } vector<string> ans; int main() { int n, m; cin >> n >> m; for (int i = 1 ;i <= n; i++) { fa[i] = i; } for (int i = 0; i < m; i++) { cin >> e[i].name; cin >> e[i].cost; cin >> e[i].u; cin >> e[i].v; } sort(e, e + m, cmp); for (int i = 0; i < m; i++) { int u = e[i].u, v = e[i].v; if (find(u) != find(v)) { ans.push_back(e[i].name); join(u, v); } } int c = 0; sort(ans.begin(), ans.end()); for (int i = 1; i <= n; i++) { if (find(i) == i)c++; } if(c==1)for (auto p : ans)cout << p; else cout << "don't cout or printf me directly!!!!!!!\\Orz/"; }
L3 top
Is L3-1 a legal heap
Author: Bei
Topic report
summary
- People think this question is an excellent one. The extremely strong ladder style combines the data structure in the class, and it is also difficult
- At that time, there were five people in the problem group. Apart from me, there were three other people to test the problem. Jie god was A in 40 minutes. Xun and Chi God struggled for A long time, and finally they were A.
- At first, Mr. Xun gave a wrong practice without technical content, and then got more than 20 points
- I just remembered that I was in a bad state that day when I worked out the data. I did it casually. Except for this problem, I didn't write out the problem analysis, I wrote all the other problems, so I resolutely recreated a stronger and more comprehensive data
- After all, now that the ABC question group is in power, there can be no mistakes like the previous ABA question group and AB question group (dog head saves life)
- So later, I spent another whole night writing six data generation codes and creating an extremely comprehensive data.
- Good trials are inseparable from excellent topics and data.
Data analysis
-
I made four data for this problem, and each data has T = 10 T=10 T=10, which is designed to prevent pure YES and NO from cheating points
-
Test point 1, small data n ∈ [ 100 , 200 ] n \in [100,200] n ∈ [100200], test the basic correct properties of the algorithm. For the implementation of this problem, even if each point is soaked once as the root node, 10 points
-
The above is the code I used to generate the data of the test point, which is composed of 10 small data. Small represents this small data point, and others can understand it by themselves according to the English meaning
-
Test point 2, big data, for some n ∈ [ 2 15 + 2 7 , 2 16 − 2 7 ] n \in [2^{15}+2^7,2^{16}-2^7] n ∈ [215 + 27216 − 27], another part n ∈ [ 5 e 4 , 1 e 5 ] n \in [5e4,1e5] n ∈ [5e4,1e5], the general direction of the algorithm is basically correct, 9 points
-
Test point 3, big data, for some n ∈ [ 2 15 + 2 7 , 2 16 − 2 7 ] n \in [2^{15}+2^7,2^{16}-2^7] n ∈ [215 + 27216 − 27], another part n ∈ [ 5 e 4 , 1 e 5 ] n \in [5e4,1e5] n ∈ [5e4,1e5], if one group contains a chain, if the first step is direct recursive search, the stack will overflow because the number of recursive layers is too deep (100000 layers), and RE (segment error) will be reported, 9 points
-
President Xun and I tested the above paragraph errors together, because the original second test point also contained chains, so I finally deleted the chains in the second test point
-
The questioner believes that if the general direction is not wrong, he can also get 21 points, so as not to be stuck by this chain, which is more differentiated
-
Test point 4, boundary test, n ≤ 2 n \leq 2 If n ≤ 2, 2 points
-
Careful mind can get full
Difficulty analysis
- When the examiners checked the questions, they found that there was indeed some difficulty. Our problem group worked so hard, let alone other students
- So I planned to reduce the difficulty and improve the scoring rate according to the time and reaction of the tester, so I gave an explanation of the complete binary tree and a hint about the problem solution in hint
- Because it is found that four people in the problem group have made this problem, but they use three different methods, but they all use the same thing, so I give the most key tips.
- A variety of solutions have also become a feature of this problem.
- Overall, this problem is still very difficult.
- In order to improve the scoring rate, I specially changed the time limit of 600ms to 1500ms. I'm afraid someone will get stuck because cin or scanf is not fast enough. As long as the algorithm is basically correct.
Algorithm 1: pruning + heap properties + map statistics
Author: Bei
Algorithm idea
- After reading the topic at first glance, because the tree can rotate, the first reaction is generally to find all the minimum values, and then find all the maximum values, and then judge based on them. Later, it is found that there may be multiple maximum and minimum values, and construct a set of data with 5k maximum and 5k minimum values. This wrong algorithm can be stuck. Later, according to the nature of the heap, The node with edge 2 can be the root, which optimizes the algorithm
- Next, we only need to take the node with degree 2 as the root to judge whether it is a legal heap. Because of the complexity problem, the complexity of judging whether it is a large and small root heap is significantly less than that of judging whether it is a perfect binary tree, so we first judge whether it is a large and small root heap
- It's easy to judge the size of the root pile. I won't say it here
- To judge whether it is a perfect binary tree, a set of data can be constructed, as follows
1
12
1 2 3 4 5 6 7 8 9 10 11 12
1 2
1 3
2 4
2 5
3 6
3 7
4 8
4 9
6 10
7 11
7 12
- Therefore, if we only judge the bottom layer, and the left and right nodes of the tree can be interchanged, it is obviously a wrong idea. Therefore, we need to judge not only the bottom layer, but also all layers
- First, judge whether the number of nodes in all layers is legal. Here, the root node is layer 0, and num[i] represents the number of nodes in layer I. If num[i]= (1 < < I) is illegal
- num_leaf[i] stands for i i The number of leaf nodes contained in the subtree of node i
- Maintain 18 large top piles (subscript to 17)( 2 16 ≤ 2^{16}\leq 216 ≤ n Max ≤ 2 17 \leq 2^{17} ≤217)
- Let x = 1 < < (D-I), nsum represents the number of leaf nodes of the whole tree, and one layer is represented by a heap for nodes i i I put num_leaf[i] in the h [ i ] h[i] h[i] heaps, where h [ i ] h[i] h[i] stands for the third party i i What layer are i nodes on
- Theoretically, the same layer should have only several x values and at most one non-x value (i.e. nsum%x)
- It can be understood that the number of leaf nodes is taken as a tag number X. if it is a legal perfect binary tree, for each layer, there is only one non-x value at most
- We can judge whether it is a perfect binary tree with the help of 18 large top heaps or 18 maps (because there are only 2 values at most in the same layer)
- As for how to judge the method of large top heap, if top()=x, pop directly. Otherwise, there is only one case, that is, top()=nsum%x and heap.size()==1
- The two values stored in the map here are much faster than those here, and they are easy to write. I won't repeat them here, as above.
code implementation
#include <bits/stdc++.h> #include <unordered_map> #include <unordered_set> using namespace std; #define el '\n' #define eb push_back typedef vector<int> vci; const int N = 1e5 + 10; int T, n, m, x, y, k, a[N], num[N], h[N], num_leaf[N], d; vci v[N], rt, mx_id, mi_id; //rt stores the number of nodes with more than 2 edges bool flag = 0, vis[N]; bool check_mi(int u, int fa) { //Determine whether it is a small root pile for (int i = 0; i < v[u].size(); i++) { int t = v[u][i]; if (t == fa) continue; if (a[t] < a[u]) return 0; if (!check_mi(t, u)) return 0; } return 1; } bool check_mx(int u, int fa) { //Determine whether it is a large root heap for (int i = 0; i < v[u].size(); i++) { int t = v[u][i]; if (t == fa) continue; if (a[t] > a[u]) return 0; if (!check_mx(t, u)) return 0; } return 1; } bool check_num() { for (int i = 1; i <= n; i++) { if (v[i].size() > 3) return 0; //More than three sides must not be the root of a binary tree if (v[i].size() == 2) { rt.eb(i); } } if (rt.size() > 2) return 0; //There are at most two sz==2 in a heap return 1; } void get_leaf(int u) { //Gets the number of leaf nodes in layer d (the lowest layer) in the tree with the current node as the root num_leaf[u] = 0; //Multiple groups of samples, initial value if (h[u] == d) { //Leaf node num_leaf[u] = 1; return; } for (int i = 0; i < v[u].size(); i++) { int t = v[u][i]; if (!vis[t]) { vis[t] = 1; get_leaf(t); num_leaf[u] += num_leaf[t]; //Plus num_l[] } } } bool check_per(int u) { //Determine whether it is a perfect binary tree if (n <= 2) return 1; //Below two nodes, it must be queue<int> q; q.push(u); memset(vis, 0, sizeof vis); memset(h, 0, sizeof h); memset(num, 0, sizeof num); vis[u] = 1; h[u] = 0; num[0] = 1; while (!q.empty()) { //bfs search for node depth int t = q.front(); q.pop(); for (int i = 0; i < v[t].size(); i++) { int w = v[t][i]; if (!vis[w]) { vis[w] = 1; q.push(w); h[w] = h[t] + 1; num[h[w]]++; //Number of nodes in the current layer } } } d = log(n) / log(2); //Maximum depth for (int i = 1; i < d; i++) { if (num[i] != (1 << i)) return 0; //Layer i has (2^i) nodes } memset(vis, 0, sizeof vis); vis[u] = 1; get_leaf(u); //Gets the number of leaf nodes in layer d (the lowest layer) in the tree with the current node as the root // Priority_queue < int > heap [18]; / / the ith large top heap stores num_l [] map<int, int> mp[18]; for (int i = 1; i <= n; i++) if (num_leaf[i]) mp[h[i]][num_leaf[i]] ++ ; int nsum = n - (1 << d) + 1; //There are (1 < < d) - 1 nodes in the first d-1 layer. Subtract n to get the number of nodes in the last layer, i.e. num[d]..... i m sb for (int i = d - 1; i >= 0; i--) { x = 1 << (d - i); if(mp[i].size() > 2) { return 0; } else { for(auto it : mp[i]) { if(it.first !=x ) { if(it.second > 1 || it.first != nsum % x) return 0; } } } } return 1; } void check() { //Check whether it is a legal heap for (int i = 0; i < rt.size(); i++) if (check_mi(rt[i], 0) || check_mx(rt[i], 0)) //Check whether it is a small root pile or a large root pile if (check_per(rt[i])) { //Check whether it is a perfect binary tree flag = 1; return; } } void Pr(bool f) { //Output results if (f) cout << "YES" << el; else cout << "NO" << el; } int main() { cin.tie(0), cout.tie(0), cin.sync_with_stdio(false); cin >> T; while (T--) { cin >> n; rt.clear(); //initial value for (register int i = 1; i <= n; i++) v[i].clear(); //initial value for (register int i = 1; i <= n; i++) //Input weight cin >> a[i]; for (register int i = 1; i <= n - 1; i++) { //Read in tree structure cin >> x >> y; v[x].eb(y), v[y].eb(x); } if (n <= 2) { Pr(1); continue; } if (!check_num()) //Check whether the number of connected edges of nodes is legal Pr(0); else { flag = 0; check(); Pr(flag); } } }
Algorithm 2: pruning + recursion + large and small root heap
Tested by: Xun
meaning of the title
Given a tree, judge whether there is at least one organization form of binary tree so that it is a large / small root heap.
Problem solving ideas
First of all, it's not hard to think of starting from the root node. We can enumerate the root nodes, but directly enumerating each point is obviously too complex (if you can't think of how to optimize, you can get a lot of points if the subsequent processing is correct) Here, we consider the nature of a heap: it is a complete binary tree. The specific definition of a complete binary tree can be Baidu by itself, but I won't repeat it here.
So we come to the first difficulty of this problem - how to prune and reduce enumeration: as a complete binary tree, there are at most two nodes whose degree is not 3 (that is, the root node and the last node of sequence traversal), otherwise, it is not a complete binary tree.
At the same time, the root node must be a node with degree 2, that is, the enumeration times of the root node is at most 2. If it is found that it is not a complete binary tree, the error can be output directly.
Therefore, in the first step, we calculate the degree of each node and record the nodes with degree 2. If we find that the number of nodes with degree 2 is not within the range of [1,2], we can directly output unsatisfied (if there are other conditions that do not meet the binary tree, for example, nodes with degree > 3 or degree = 0 can also directly output unsatisfied, so as to ensure that they are a binary tree for subsequent processing) .
Then the problem becomes: given the root node, how to judge a tree as large / small root heap.
According to the properties of large / small root heap given in the title, we can divide it into two problems:
- Is it a complete binary tree
- The weight of each node shall not be less than (≥) or greater than (≤) the value of all its child nodes.
I won't say much about the second problem. Given the root node, simply make two recursive judgments (large root heap and small root heap respectively).
Then come to the second difficulty of this problem: how to judge whether a tree is a complete binary tree. This problem itself is not difficult, but the key is that the binary tree of this problem does not explicitly limit the left and right subtrees, and only gives its two sons (or one) , the left subtree and the right subtree can be exchanged, as long as there is any way to determine the subtree so that it is a complete binary tree.
Here are the author's ideas (welcome to join your own thinking, find out your mistakes and learn together):
Recursively judge that at most one of the two children of each node can be an imperfect binary tree. If both subtrees are imperfect binary trees, it is not allowed. If only one subtree is imperfect, that subtree must be a higher subtree
Recursively judge from the root node down, constantly maintain the "number of imperfect subtrees k" (k ∈ {0,1,2}) of each node, and discuss the classification of nodes with different number of subtrees (during recursion, the parent node needs to be recorded to form a parent-child structure, otherwise it will go back)
-
If the node has no subtree, k is 0
-
If the node has only one subtree, then k is 1
-
The node has two subtrees. K is the sum of K of the left and right subtrees. If k=2, it is directly marked as not a heap. Otherwise, judge whether the height of the left and right subtrees is the same. If different:
- Left subtree height > right subtree height. Judge whether the k of the right subtree is 0 (that is, whether the right subtree is perfect). If it is not 0, it will be directly marked as not a heap. If it is 0, its k will be modified to 1
- The height of the right subtree > the height of the left subtree. Judge whether the k of the left subtree is 0 (that is, whether the left subtree is perfect). If it is not 0, it will be directly marked as not a heap. If it is 0, its k will be modified to 1
In short:
1. Prune and enumerate root nodes
2. Judge whether it is large / small root pile
3. Judge whether it is a perfect binary tree
code
#include<bits/stdc++.h> using namespace std; #define ll long long //Who wrote the question! Write a solution and write a few k words!!!!! //First, rule out what Beibei said typedef pair<int, int> PLL; #define inf 0x3f3f3f3f const int maxn = 100000 + 10; int a[maxn]; int vis[maxn]; int wanmei[maxn]; vector<int>g[maxn]; vector<int>root; bool check1(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] < a[u]) return false; if (!check1(it, u)) return false; } return true; } bool check2(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] > a[u]) return false; if (!check2(it, u)) return false; } return true; } bool ans = true; int check_wanmei(int root, int& f) {//0 does not have 1 more than 2 vector<int>x; for (auto it : g[root]) { if (!wanmei[it]) { x.push_back(it); wanmei[it] = 1; } } if (x.size() == 2) { int f1, f2; int dep1, dep2; dep1 = check_wanmei(x[0], f1); dep2 = check_wanmei(x[1], f2); f = f1 + f2; if (f == 2)ans = false; //if (abs(dep1 - dep2) > 2)ans = false; if (dep1 == dep2); else if (dep1 < dep2) { if (f == 0)f = 1; else if (!f1)ans = false; } else if (dep1 > dep2) { if (f == 0)f = 1; else if (!f2)ans = false; } return min(dep1, dep2) + 1; } else if (x.size() == 1) { f = 1; int ff; return check_wanmei(x[0], ff); } else if (x.size() == 0) { f = 0; return 0; } return 0; } bool check_manzu(int root) { memset(vis, 0, sizeof(vis)); queue<int>q; vis[root] = 1; q.push(root); q.push(-1); int flag = 0, flag2 = 0; while (q.size()) { int u = q.front(); q.pop(); if (u == -1) { if (q.size() == 0)break; if (flag || flag2)break; q.push(-1); continue; } int num = 0; for (auto it : g[u]) { if (!vis[it]) { num++; q.push(it); vis[it] = 1; } } if (num == 1) { if (flag)return false; else flag++; } else if (num == 0) { flag2++; } } if (flag > 1)return false; while (q.size()) { int u = q.front(); q.pop(); int num = 0; for (auto it : g[u]) { if (!vis[it]) { num++; q.push(it); vis[it] = 1; } } if (num != 0)return false; } return true; } bool check_wanquan(int root) { memset(wanmei, 0, sizeof(wanmei)); ans = true; int f; wanmei[root] = 1; if(check_manzu(root)){ check_wanmei(root, f); return ans; } else { return false; } } bool check(int n) { if(n<=2)return true; root.clear(); for (int i = 1; i <= n; i++) { if (g[i].size() > 3) return false; if (g[i].size() == 2) root.push_back(i); } if (root.size() > 2) return false; for (auto it : root) { if (!check_wanquan(it))continue; if (check1(it, 0) || check2(it, 0))return true; } return false; } int main() { int t; cin >> t; while (t--) { int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; g[i].clear(); } for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); } if (check(n))cout << "YES" << endl; else cout << "NO" << endl; //if (check_wanquan(1))cout << "YES" << endl; //else cout << "NO" << endl; } }
Algorithm 3: height difference + recursion
Tested by: Jie
Algorithm idea
First determine the root: what kind of point may become the root: the point with the smallest or largest weight, and its degree can only be 2 (because the heap is a binary heap) . if the tree is a legitimate heap, there can obviously be only two points with a degree of 2. If there are more than two, the tree cannot be a binary heap. Otherwise, take these points as the roots and try to build a legitimate heap. Another special judgment of N < = 2 must be a legitimate heap
int solve() { if(n<=2)return 1; int num=0; for(int i=1; i<=n; i++) { if(vec[i].size()==2)num++; if(vec[i].size()>3)return 0; } if(num>2)return 0; for(int i=1; i<=n; i++) { if(vec[i].size()==2&&a[i]==minval) { flag=1; dfs(i,0,0);//Small root pile start if(flag==1)return 1; } if(vec[i].size()==2&&a[i]==maxval) { flag=1; dfs(i,0,1);//Big root pile start if(flag==1)return 1; } } return 0; }
The left and right subtrees cannot be dissatisfied at the same time: the following full refers to the full binary tree. For a complete binary tree, consider the root u of the complete binary tree, which needs to meet: the left subtree is a full binary tree, the right subtree is a complete binary tree, or the left subtree is a complete binary tree, and the right subtree is a full binary tree. And because the left and right subtrees of each point can be exchanged arbitrarily, it only needs to meet that at least one of the left and right subtrees of each point is a full binary tree. That is, the left and right subtrees cannot be dissatisfied at the same time.
The height difference between the left and right subtrees cannot be greater than 1: obviously, for a complete binary tree, the height of the left subtree is at most 1 higher than that of the right subtree.
How to judge a tree is a full binary tree? The left and right subtrees are full binary trees with the same height (this can ensure that the full binary tree size of the subtree is the same).
code implementation
#include<bits/stdc++.h> using namespace std; const int N=1e5+100; vector<int>vec[N]; int a[N],tag,flag,high[N],siz[N],full[N],n,minval,maxval; void dfs(int u,int fa,int key) {//key=0/1 indicates that small root heap / large root heap is being judged vector<int>son; high[u]=siz[u]=1; for(int i=0; i<vec[u].size(); i++) { int v=vec[u][i]; if(v==fa)continue; if(key==0) { if(a[v]<a[u]) { flag=0; return; } } else { if(a[v]>a[u]) { flag=0; return; } } dfs(v,u,key); siz[u]+=siz[v]; high[u]=max(high[u],high[v]+1); son.push_back(v); } if(siz[u]==1)full[u]=1;//A complete binary tree with one node else { if(full[son[0]]==1&&full[son[1]]==1&&high[son[0]]==high[son[1]])full[u]=1;//If the left and right subtrees are full and have the same height, the tree is also a complete binary tree else full[u]=0; } if(son.size()==0)return; if(son.size()==1) { if(high[son[0]]!=1)flag=0; return; } if(son.size()==2) { if(abs(high[son[0]]-high[son[1]])>1){ flag=0; return; } if(full[son[0]]==0&&full[son[1]]==0) { flag=0; return; } } } int solve() { if(n<=2)return 1; int num=0; for(int i=1; i<=n; i++) { if(vec[i].size()==2)num++; if(vec[i].size()>3)return 0; } if(num>2)return 0; for(int i=1; i<=n; i++) { if(vec[i].size()==2&&a[i]==minval) { flag=1; dfs(i,0,0);//Small root pile start if(flag==1)return 1; } if(vec[i].size()==2&&a[i]==maxval) { flag=1; dfs(i,0,1);//Big root pile start if(flag==1)return 1; } } return 0; } int main() { int T,u,v; cin>>T; while(T--) { cin>>n; minval=1e9; maxval=-1e9; for(int i=1; i<=n; i++) { scanf("%d",&a[i]); minval=min(minval,a[i]); maxval=max(maxval,a[i]); vec[i].clear(); full[i]=0; } for(int i=1; i<n; i++) { scanf("%d%d",&u,&v); vec[u].push_back(v); vec[v].push_back(u); } if(solve())cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }
Algorithm 4: tree depth + recursion + size root heap
Tested by: Chi
Algorithm idea
-
Because it requires a complete binary tree, there are only two edges connected to the root node (in the case of only one point and two, because it must be a complete binary tree at this time). According to this property, all possible root nodes can be filtered out, and then the past can be judged one by one.
-
First judge whether it is a complete binary tree, and then judge whether all child nodes are greater than or less than the parent node
-
The form of complete binary tree is that except that the last layer is a full binary tree, and then all nodes of the last layer are on the left. We judge whether the class tree meets the condition except that the last layer is a full binary tree according to the level traversal.
When a node with only one or no child nodes in a layer is found, take the current layer as the penultimate layer and judge the nodes of the current layer and the next layer.
Because there is no provision for left and right nodes, and both sides can be changed at will, the above judgment can not determine whether all nodes in the last layer can be placed on the far left, nor whether there is only one node with only one child node. A specific judgment is needed (in fact, two judgments can be combined and written at one time, but I always write wa)
A recursive judgment records the number of only one node subordinate to the left and right child nodes, and then judges that if the number exceeds 1, it is wrong, and if the depth of the existing subtree is shallow (the subtree of only one node must extend to the last layer, so the depth is the deepest).
int check_wanmei(int root, int& f) { //0 has no only one node 1, one has only one node 2, and more than one node returns the depth vector<int>x; for (auto it : g[root]) { if (!wanmei[it]) { x.push_back(it); wanmei[it] = 1; } } if (x.size() == 2) { int f1, f2; int dep1, dep2; dep1 = check_wanmei(x[0], f1); dep2 = check_wanmei(x[1], f2); f = f1 + f2; if (f == 2)ans = false;//There are multiple nodes with only one child if (dep1 == dep2);//When the depth is the same, only the left and right subtrees of one child node can be interchanged. There is no requirement else if (dep1 < dep2) {//The depth of subtree with only one child node is required to be deeper if (f == 0)f = 1; else if (!f1)ans = false; } else if (dep1 > dep2) { if (f == 0)f = 1; else if (!f2)ans = false; } return min(dep1, dep2) + 1; } else if (x.size() == 1) { f = 1; int ff; return check_wanmei(x[0], ff);//This particular point is not depth } //Don't worry about the depth problem caused by the situation that this place has been down several times. In the previous judgment, it has been guaranteed that the last layer is a full binary tree else if (x.size() == 0) { f = 0; return 0; } return 0; }
-
Then judge whether the weight of each node should not be less than (≥) or greater than (≤) the values of all its child nodes.
bool check1(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] < a[u]) return false; if (!check1(it, u)) return false; } return true; } bool check2(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] > a[u]) return false; if (!check2(it, u)) return false; } return true; }
code implementation
#include<bits/stdc++.h> using namespace std; #define ll long long typedef pair<int, int> PLL; #define inf 0x3f3f3f3f const int maxn = 100000 + 10; int a[maxn]; int vis[maxn]; int wanmei[maxn]; vector<int>g[maxn]; vector<int>root; bool check1(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] < a[u]) return false; if (!check1(it, u)) return false; } return true; } bool check2(int u, int fa) { for (auto it : g[u]) { if (it == fa) continue; if (a[it] > a[u]) return false; if (!check2(it, u)) return false; } return true; } bool ans = true; int check_wanmei(int root, int& f) {//0 does not have 1 more than 2 vector<int>x; for (auto it : g[root]) { if (!wanmei[it]) { x.push_back(it); wanmei[it] = 1; } } if (x.size() == 2) { int f1, f2; int dep1, dep2; dep1 = check_wanmei(x[0], f1); dep2 = check_wanmei(x[1], f2); f = f1 + f2; if (f == 2)ans = false; //if (abs(dep1 - dep2) > 2)ans = false; if (dep1 == dep2); else if (dep1 < dep2) { if (f == 0)f = 1; else if (!f1)ans = false; } else if (dep1 > dep2) { if (f == 0)f = 1; else if (!f2)ans = false; } return min(dep1, dep2) + 1; } else if (x.size() == 1) { f = 1; int ff; return check_wanmei(x[0], ff); } else if (x.size() == 0) { f = 0; return 0; } return 0; } bool check_manzu(int root) { memset(vis, 0, sizeof(vis)); queue<int>q; vis[root] = 1; q.push(root); q.push(-1); int flag = 0, flag2 = 0; while (q.size()) { int u = q.front(); q.pop(); if (u == -1) { if (q.size() == 0)break; if (flag || flag2)break; q.push(-1); continue; } int num = 0; for (auto it : g[u]) { if (!vis[it]) { num++; q.push(it); vis[it] = 1; } } if (num == 1) { if (flag)return false; else flag++; } else if (num == 0) { flag2++; } } if (flag > 1)return false; while (q.size()) { int u = q.front(); q.pop(); int num = 0; for (auto it : g[u]) { if (!vis[it]) { num++; q.push(it); vis[it] = 1; } } if (num != 0)return false; } return true; } bool check_wanquan(int root) { memset(wanmei, 0, sizeof(wanmei)); ans = true; int f; wanmei[root] = 1; if(check_manzu(root)){ check_wanmei(root, f); return ans; } else { return false; } } bool check(int n) { if(n<=2)return true; root.clear(); for (int i = 1; i <= n; i++) { if (g[i].size() > 3) return false; if (g[i].size() == 2) root.push_back(i); } if (root.size() > 2) return false; for (auto it : root) { if (!check_wanquan(it))continue; if (check1(it, 0) || check2(it, 0))return true; } return false; } int main() { int t; cin >> t; while (t--) { int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; g[i].clear(); } for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); } if (check(n))cout << "YES" << endl; else cout << "NO" << endl; //if (check_wanquan(1))cout << "YES" << endl; //else cout << "NO" << endl; } }
Daily work of L3-2 company employees
preface
To find the k-th largest, it is easy to think of the weight line segment tree to find the total k-th largest and the chairman tree to find the interval k-th largest.
Method 1: weight segment tree + segment tree merging
Train of thought analysis
Considering that the subtree of each point is a weight line segment tree, then l o g n logn logn ^ find the k-th largest of the subtree. How to quickly find the weight line segment tree composed of the subtree of each node u? The segment trees can be merged. After merging the weight segment trees of all subtrees of u, the node information of u can be modified and added at a single point.
Realization idea
Author: Jie
- 1. The data ensures that each employee has different competency values, and the competency value is < = n, then the competency values of all employees are actually an arrangement of 1-n. Because the weight segment tree only records the capability value, there must be an array id to locate the capability value to the person, that is i d [ i ] id[i] id[i] indicates the number of the person whose ability value is I.
- 2. How to find the k-th largest interval in the weighted line segment tree? It can be split in two on the line segment tree.
- 3. As for segment tree merging, it is actually a template.
code implementation
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; struct edge{ int v,next; }e[N*2]; int vex[N],tot,k[N],a[N],id[N]; struct Tree{ int l,r,v; }tree[N*4]; int cnt,n,size[N],root[N],ans[N]; void add(int u,int v){ tot++; e[tot].v=v; e[tot].next=vex[u]; vex[u]=tot; } void pushup(int now){ tree[now].v=tree[tree[now].l].v+tree[tree[now].r].v; } int change(int now,int l,int r,int x,int k) { if(now==0) now=++cnt; if(l==r) { tree[now].v+=k; return now; } int mid=(l+r)/2; if(x<=mid) tree[now].l=change(tree[now].l,l,mid,x,k); else tree[now].r=change(tree[now].r,mid+1,r,x,k); pushup(now); return now; } int merge(int a,int b,int l,int r) { if(!a) return b; if(!b) return a; if(l==r) { tree[a].v+=tree[b].v; return a; } int mid=(l+r)/2; tree[a].l=merge(tree[a].l,tree[b].l,l,mid); tree[a].r=merge(tree[a].r,tree[b].r,mid+1,r); pushup(a); return a; } int query(int now,int l,int r,int k){ if(l==r)return l; int mid=(l+r)/2; if(k<=tree[tree[now].l].v)return query(tree[now].l,l,mid,k); else return query(tree[now].r,mid+1,r,k-tree[tree[now].l].v); } void dfs(int u,int fa){ size[u]=1; for(int i=vex[u];i;i=e[i].next){ int v=e[i].v; if(v==fa)continue; dfs(v,u); root[u]=merge(root[u],root[v],1,n); size[u]+=size[v]; } root[u]=change(root[u],1,n,a[u],1); ans[u]=id[query(root[u],1,n,k[u])]; } int main(){ int u; cin>>n; for(int i=1;i<=n;i++){ cin>>u; add(i,u); add(u,i); } for(int i=1;i<=n;i++){ cin>>a[i]; id[a[i]]=i; } for(int i=1;i<=n;i++)cin>>k[i]; dfs(1,0); for(int i=1;i<=n;i++)cout<<ans[i]<<" "; return 0; }
Method 2: dfs order + chairman tree
Author: Jie
Train of thought analysis
conduct d f s dfs In the new sequence generated by dfs {order, the subtree of each point is an interval in the sequence. Then the problem becomes the k-th largest interval of the new sequence of n queries·
Realization idea
-
- 1. First d f s dfs dfs gets this over and over again d f s dfs dfs order
- 2. With this d f s dfs Establishing chairman tree in dfs # sequence
- 3. For each point in sequence, use the chairman tree to find the k-th largest interval
code implementation
#include<bits/stdc++.h> using namespace std; const int N=1e6+10; struct edge{ int v,next; }e[N*2]; int vex[N],tot,k[N],a[N]; struct Tree{ int l,r,v; }tr[N*4]; int cnt,n,in[N],out[N],root[N],seg[N],id[N],top; void add(int u,int v){ tot++; e[tot].v=v; e[tot].next=vex[u]; vex[u]=tot; } void pushup(int now){ tr[now].v=tr[tr[now].l].v+tr[tr[now].r].v; } int build(int now,int l,int r){ now=++cnt; if(l==r){ tr[now].v=0; return now; } int mid=(l+r)/2; tr[now].l=build(tr[now].l,l,mid); tr[now].r=build(tr[now].r,mid+1,r); pushup(now); return now; } int clone(int now){ cnt++; tr[cnt]=tr[now]; return cnt; } int update(int now,int l,int r,int x,int k){ now=clone(now); if(l==r){ tr[now].v+=k; return now; } int mid=(l+r)/2; if(x<=mid)tr[now].l=update(tr[now].l,l,mid,x,k); else tr[now].r=update(tr[now].r,mid+1,r,x,k); pushup(now); return now; } int query(int l,int r,int nowx,int nowy,int k){ if(l==r)return l; int sum=tr[tr[nowy].l].v-tr[tr[nowx].l].v; int mid=(l+r)/2; if(k<=sum)return query(l,mid,tr[nowx].l,tr[nowy].l,k); else return query(mid+1,r,tr[nowx].r,tr[nowy].r,k-sum); } void solve(){ root[0]=build(0,1,n); for(int i=1;i<=n;i++)root[i]=update(root[i-1],1,n,a[seg[i]],1); for(int i=1;i<=n;i++)cout<<id[query(1,n,root[in[i]-1],root[out[i]],k[i])]<<" "; } void dfs(int u,int fa){ in[u]=++top; seg[top]=u; for(int i=vex[u];i;i=e[i].next){ int v=e[i].v; if(v==fa)continue; dfs(v,u); } out[u]=top; } int main(){ int u; cin>>n; for(int i=1;i<=n;i++){ cin>>u; add(i,u); add(u,i); } for(int i=1;i<=n;i++){ cin>>a[i]; id[a[i]]=i; } for(int i=1;i<=n;i++)cin>>k[i]; dfs(1,0); solve(); return 0; }
Method 3: DFS sequence + dsu + balanced tree
Tested by: Bei
summary
- The examiner's first reaction is also dfs preface + chairman tree. The person who wrote the question above has written it. I won't repeat it here
- Here, the examiner gives another solution: D F S order + d s u + flat Weigh tree DFS order + dsu + balanced tree DFS order + dsu + balanced tree
Algorithm idea
- No matter what kind of practice, there is a core idea - "sub tree Mo team"
- What Mo team does is to lock each query interval in its own order. Here, the subtree is the query interval, so it is called sub tree Mo team. Moreover, they are all offline. All queries are stored first, and then processed uniformly.
- The idea of dsu on tree is to recursively calculate the answer of the light son each time (not added to the balance tree), then calculate the answer of the heavy son (added to the balance tree), and then add back the contributions of all the light sons to calculate the answer of the whole tree
- If the current subtree is not someone else's heavy son, it will be deleted from the balance tree
- It can be summarized as follows: the light son is not retained in the balance tree, and the heavy son is retained in the balance tree
- The total time complexity is O ( n log n ) O(n\log n) O(nlogn)
operating efficiency
- You can see the running results. It only takes less than 200 milliseconds
- However, the core of our investigation is the algorithm idea. We don't deliberately card everyone's constant, so we give 1000ms
code implementation
#include <bits/stdc++.h> #include <bits/extc++.h> #include <unordered_map> #include <unordered_set> using namespace std; using namespace __gnu_cxx; using namespace __gnu_pbds; #define debug(x) cerr << #x << ": " << x << '\n' #define bd cerr << "----------------------" << el #define el '\n' #define cl putchar('\n') #define pb push_back #define eb emplace_back #define x first #define y second #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define loop(i, a, b) for (int i = (a); i < (b); i++) #define dwn(i, a, b) for (int i = (a); i >= (b); i--) #define ceil(a, b) (a + (b - 1)) / b #define ms(a, x) memset(a, x, sizeof(a)) #define inf 0x3f3f3f3f #define db double typedef long long LL; typedef long double LD; typedef pair<int, int> PII; typedef pair<db, db> PDD; typedef vector<int> vci; inline int read() { int f = 1, d = 0; char ch; ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') d = (d << 1) + (d << 3) + (ch ^ 48), ch = getchar(); return d * f; } const int N = 1e5 + 10, M = 2e6 + 10, E = 1e3 + 10, md = 1e9 + 7; const double PI = acos(-1), eps = 1e-8; int T, n, m, k; LL a[N]; vector<int> g[N]; int qry[N], ans[N]; typedef tree<PII, null_type, less<PII>, rb_tree_tag, tree_order_statistics_node_update> Tree; Tree tr; int dfn[N], iDfn[N], sz[N], son[N], dfs_clock; void merge(int cur, bool isSon) { for (int ver : g[cur]) if (ver != son[cur]) merge(ver, false); if (son[cur]) //If a heavy son exists merge(son[cur], true); for (int ver : g[cur]) if (ver != son[cur]) for (int i = dfn[ver]; i < dfn[ver] + sz[ver]; i++) tr.insert(make_pair(a[iDfn[i]], iDfn[i])); tr.insert(make_pair(a[cur], cur)); ans[cur] = (*tr.find_by_order(qry[cur] - 1)).second; if (!isSon) //If it's not a heavy son for (int i = dfn[cur]; i < dfn[cur] + sz[cur]; i++) tr.erase(make_pair(a[iDfn[i]], iDfn[i])); } void dfs(int cur, int pre) //Generate dfs order { dfn[cur] = ++dfs_clock; iDfn[dfs_clock] = cur; sz[cur] = 1; for (int ver : g[cur]) { dfs(ver, cur); sz[cur] += sz[ver]; //Calculates the size of the current subtree if (!son[cur] || sz[ver] > sz[son[cur]]) son[cur] = ver; //Looking for a heavy son } } int main() { cout.tie(0); n = read(); rep(i, 1, n) { k = read(); g[k].pb(i); } rep(i, 1, n) a[i] = read(); rep(i, 1, n) qry[i] = read(); dfs(1, 0); //Get dfs order merge(1, 1); //By default, 1 is also a duplicate son, omitting the last deletion and optimizing for (int i = 1; i <= n; i++) cout << ans[i] << ' '; }
L3-3 triplet
Author: Bei
Author's Report
- Originally, I put this question as a delivery proposition. It also requires a deep level of algorithm (may, the first level of selection?) to write violence forcibly. It takes about 70 minutes to get 5 points. If the problem is solved correctly, it will not be done if the problem is solved.
- But then I changed the question. After all, the ABC question group is in power now, not to embarrass everyone, but to produce a high-quality paper with distinction.
- So I changed to this one. As for the original question, I got the follow-up to you.
- I set up four test points
- Test point 1: n 1 = n 2 = n 3 = 50 n_1=n_2=n_3=50 n1 = n2 = n3 = 50, tripartite violence can pass, 2 points
- Test point 2: n 1 = 100 , n 2 = 1000 , n 3 = 1 e 5 n_1=100,n_2=1000,n3=1e5 n1 = 100,n2 = 1000,n3=1e5, mustset can pass, 5 points
- Test point 3: n 1 = 32343 , n 2 = 50097 , n 3 = 96675 n_1=32343,n_2 = 50097,n_3=96675 n1 = 32343,n2 = 50097,n3 = 96675, FFT can pass, 11 points
- Test point 4: n 1 = n 2 = n 3 = 1 e 5 n1=n2=n3=1e5 n1=n2=n3=1e5, the FFT is OK, the long long (WA) is stuck out, and the FFT template (TLE) with too large constant, 12 points.
Algorithm idea
Algorithm 1: cubic violence, 2 points
- There's nothing to say. There are three cycles without brain
- Time complexity O ( n 3 ) O(n^3) O(n3)
code implementation
#define el '\n' #define rep(i, a, b) for (int i = (a); i <= (b); i++) int main() { cin.tie(0); cout.tie(0); cin >> n1; rep(i, 1, n1) cin >> a[i]; cin >> n2; rep(i, 1, n2) cin >> b[i]; cin >> n3; rep(i, 1, n3) cin >> c[i]; LL cnt = 0; rep(i, 1, n1) rep(j, 1, n2) rep(k, 1, n3) if(a[i] + b[j] + c[k] == 0) cnt ++ ; cout << cnt << el; }
Algorithm 2: multiset, 7 points
- Because the array itself may have the same value, we should use multiset. If there are only two arrays, how can we use mutiset?
*Find the opposite number! - So we can n 2 n^2 n2 in m u l t i s e t multiset multiset, and then look it up
- The range given in hint is convenient for players. Put those two arrays into multiset because n1*n2==n3
Complexity analysis
- Time complexity, will a , b a,b a. B insert the elements in the array m u l t i s e t multiset Required in multiset n 1 × n 2 n_1\times n_2 n1 × n2 times, total queries required n 3 n_3 n3 # times, and the operations of inserting and querying are O ( log ( n 1 × n 2 ) ) O(\log (n_1\times n_2)) O(log(n1 × n2), the total time complexity is O ( n 1 × n 2 × log ( n 1 × n 2 ) + n 3 log ( n 1 × n 2 ) ) O(n_1 \times n_2 \times \log (n_1\times n_2) + n_3 \log (n_1 \times n_2)) O(n1×n2×log(n1×n2)+n3log(n1×n2))
- Of course, at the end of the title, I give the data, this way m u l t i s e t multiset multiset is n 1 , n 2 n_1,n_2 n1, n2 because there is a data in the title n 1 × n 2 = n 3 n_1\times n_2 =n_3 n1 × n2 = n3. I also hope some first-team seed players can calculate the complexity (I originally planned to release n 1 , n 3 n_1,n_3 n1, n3 , because people's habitual thinking is to put two consecutive ones n 1 , n 2 n_1,n_2 n1, n2, or n 2 , n 3 n_2,n_3 n2, n3 , so it's hard to get these 7 points. Finally, I didn't do that. The difficulty of violence is reduced a little. Otherwise, only the seed players of the first team can calculate the corresponding complexity and get the 7 points of violence)
code implementation
multiset<int> s; #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define el '\n' int main() { cin.tie(0); cout.tie(0); cin >> n1; rep(i, 1, n1) cin >> a[i]; cin >> n2; rep(i, 1, n2) cin >> b[i]; cin >> n3; rep(i, 1, n3) cin >> c[i]; rep(i, 1, n1) rep(j, 1, n2) s.insert(a[i]+b[j]); LL cnt = 0; rep(i, 1, n3) cnt += s.count(-c[i]); cout << cnt << el; }
Algorithm 3: Fast Fourier transform FFT, 30 points
- This question is not intended to give people A. after all, there are only a few schools that could have FFT
- What's more, the IOI competition system has no paper materials, and it also conforms to the positioning of L3-3 for the level of our school
- If you don't know FFT, you can baidu by yourself. There are many learning blogs on Baidu. I won't repeat them here
- The key idea of this question is: barrel
- FFT deals with polynomial multiplication
- We all know that FFT can multiply polynomials O ( n 2 ) O(n^2) O(n2) acceleration is O ( n log n ) O(n \log n) O(nlogn)
- So the relationship between two numbers is additive. How to make it multiply? Put them all on the exponent, multiply the two power exponents, and add the exponents.
- In this way, two times of FFT can be A. it is worth noting that due to the existence of negative numbers, the overall translation is required. The minimum case is three - 1e5, so 0 represents - 3e5, and the real 0 is 3e5
Complexity analysis
- Time complexity, which is the complexity of FFT, O ( n log n ) O(n \log n) O(nlogn) level
code implementation
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N = 600010 * 2; const double PI = acos(-1); int n, m; struct Complex { //The plural structure is basically unchanged double x, y; Complex operator+(const Complex &t) const { return {x + t.x, y + t.y}; } Complex operator-(const Complex &t) const { return {x - t.x, y - t.y}; } Complex operator*(const Complex &t) const { return {x * t.x - y * t.y, x * t.y + y * t.x}; } } A[N], B[N], C[N]; int rev[N], bit, tot; void fft(Complex a[], int inv) { //inv represents the direction of serialization, 1 represents the change from coefficient representation to point value representation, and - 1 is the reverse; for (int i = 0; i < tot; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); for (int mid = 1; mid < tot; mid <<= 1) { auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)}); for (int i = 0; i < tot; i += mid * 2) { auto wk = Complex({1, 0}); for (int j = 0; j < mid; j++, wk = wk * w1) { auto x = a[i + j], y = wk * a[i + j + mid]; a[i + j] = x + y, a[i + j + mid] = x - y; } } } } void in(int len, Complex a[]) { for (int i = 1; i <= len; ++i) { int num; scanf("%d", &num); a[num + 100000].x += 1.0; } } void _merge(Complex a[], Complex b[], int &n, int &m) { bit = 0; while ((1 << bit) < n + m + 1) bit++; tot = 1 << bit; n = tot; rev[0] = 0; //Initialize rev for (int i = 0; i < tot; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1)); fft(a, 1), fft(b, 1); for (int i = 0; i < tot; i++) a[i] = a[i] * b[i]; fft(a, -1); //The above boards do not need to be changed, just add an initialization with bit=0 for (int i = 0; i < tot; ++i) a[i].x = 1.0 * (long long)(a[i].x / tot + 0.5); } int main() { //int k; scanf("%d",&k);// Find the number of schemes with triple sum K; int lena; scanf("%d", &lena); in(lena, A); int lenb; scanf("%d", &lenb); in(lenb, B); int lenc; scanf("%d", &lenc); in(lenc, C); //input finished lena = lenb = lenc = 200000; //The range of values is polynomial length, 2e5 buckets, subscript. Negative numbers need to be shifted to the right _merge(A, B, lena, lenb); //merge _merge(A, C, lena, lenc); //merge //solved printf("%lld", (long long)(A[300000].x)); //Because the minimum value may be the sum of three -1e5, the bucket needs to be moved to the right return 0; } /* 1 2 2 -2 -1 2 1 0 */