# Intron

This copy of the game is called PAT A. I believe that many players know this copy and some players have passed this copy. This dungeon can give junior players rich experience value, or even the key to advance. After 15 days, this player has finally finished painting this copy, leaving the copy strategy for the reference of other players.
This copy is generally divided into two parts. One is the rule problem, that is, as long as you master the movement rule of the monster, you can get points without losing blood. The other is the skill problem, which depends on the experience of fighting monsters in history or a certain talent.
There are seven types of regular questions:

1. STL (Standard Template Library)
2. Sort (insert sort, merge sort, heap sort, table sort)
3. Two branch tree, graph theory, connected body, and search set
4. BFS (width first search algorithm), DFS (depth first search algorithm)
5. Validation questions
6. Array mapping problem
7. String handling problems

There are three types of skill questions:

1. Mathematical relevance
2. Greedy algorithm
3. dynamic programming

# Strategies for solving regular problems

Regular questions are built according to the inherent framework, so there are rules to follow, and there are fixed templates to solve. As long as players remember the fixed mind method, they can break it.

## STL of strategy

STL, also known as standard template library, is a general topic, which only requires the skills that players light up in the novice village. It only requires the ability of players to combine and cast skills for slightly difficult topics. General topics are similar to the following: "1006 Sign In and Sign Out", and there is no more difficult topic than "1014 Waiting in Line". The first thing to break them is to understand the usage of Vector, Map, Pair, Set, Queue, Stack, these basic containers.

container purpose
Vector Variable length array, if you need a large array but are warned with the array definition directly, you can use it.
Map It can be used when a mapping relationship is needed, such as the direct association between a person's identity ID and the person's entire file. Map can also be divided into two types: map, unordered_map. Map has the function of automatic sorting, unordered_map does not have its efficiency, but the corresponding speed will be faster. If it exceeds the time limit, you can try to change it to unordered_map.
Pair Also known as key value pair, if you only want to use the key value pair, but do not need the hash function of Map to try. Generally, it can be used in combination with Set, Vector and other containers. Set < pair < key, value > > and vector < pair < key, value > >
Set Filter containers help you filter out the same elements automatically, which is convenient for de duplication. Set can also be divided into two types: set, unordered_set, if you do not need to sort, use unordered_set increases speed
Queue The first in, first out characteristics of queue containers make this weapon generally applicable to: 1. BFS, 2. A batch of data processed in the same process (1056 rice and rice)
Stack Stack, the first in and then out feature, not many scenarios can use this weapon directly, but this feature is the basis of DFS, recursive algorithm, etc., so you can understand it

STL type questions can be divided into several types: queuing questions, statistical questions, simulation questions.
Queuing questions and statistical questions need a certain object-oriented thinking, that is, to construct objects through struct constructs, such as 1014 Waiting in Line

```struct window
{
int endtime;  //End time
int capacity; //capacity
queue<int> cust;  //Customer queue
};
```

This structure is the object of the bank counter constructed, which is more convenient for players to think according to the three-dimensional thinking mode, without first converting the thinking into the program language and then sorting out the complex relations in the topic meaning from the program language.
For example, 1051 Pop Sequence is a kind of simulation of stack. As long as the definition of stack is clear and the stack is first in and then out, you can simulate the operation of entering and leaving stack with program.
The main breakthrough points of STL are as follows:

• Build the structure body, can build the structure model of the object according to the topic meaning
• Suitable containers for objects can be selected
• Can modify the sorting mode of the corresponding container

The first two points only need to be able to read the questions and know the characteristics of each container. The third point is to modify the sorting mode of the corresponding container, which is different for different containers.
Array, Vector, Dequeue:

```#include <algorithm>
bool cmp(int a, int b){
return a > b;
}
int main(){
int arr[] = {3,1,2,5,4};
sort(arr, arr+5, cmp);
}
```

Set (there are red black trees inside, which will be sorted automatically when inserting):

```struct Node {
value;
bool operator < const(Node& a) const{
return value < a.value;
}
}
int main(){
set<Node> s;
s.insert(Node{ 0 });
s.insert(Node{ 1 });
}
```

Priority_queue:

• Definition method:
```priority_queue<int> q;
priority_queue<int, vector<int>, less<int>> q;
//Vector < int > the container that hosts the underlying data structure heap.
//Less < int > indicates that the priority of large number is large.
//Greater < int > indicates that a small number has a small priority.
```
```struct fruit{
string name;
int price;
friend bool operator < (fruit f1, fruit f2){
//High price priority
return f1.price < f2.price;
//Low price, high priority
//return f1.price > f2.price;
}
}
int main(){
priority_queue<fruit> q;
}
```
```struct cmp{
bool operator() (fruit f1, fruit f2){
return f1.price > f2.price;
}
}
int main(){
priority_queue<fruit, vector<fruit>, cmp> q;
}
```

## Order of strategy

This sort of questions can be divided into four categories, and the templates are as follows:

• Insert sort
``` const int maxN = 1000010;
int arr[maxN];
void insertSort(){
for(int i=1; i<maxN; i++){
int j=i;
int tmp = arr[i];
while(j > 0 && tmp > arr[j]){
arr[j] = arr[j-1];
j--;
}
arr[j] = tmp;
}
}
```
• Merge sort
```const int maxN = 100010;
int arr[maxN];
void merge(int L1, int R1, int L2, int R2){
int i = L1;
int j = L2;
int idx = 0;
int tmp[maxN];
while(i<=R1 && j<=R2){
if(arr[i]<arr[j]){
tmp[idx++] = arr[i];
}else{
tmp[idx++] = arr[j];
}
}
while(i<=R1){
tmp[idx++] = arr[i];
}
while(j<=R2){
tmp[idx++] = arr[j];
}
for(int k=0; k<idx; k++){
arr[L1+k] = tmp[k];
}
}
void mergeSort(){
for(int step=2; step/2<maxN; step*=2){
for(int i=0; i<N; i+=step){
int mid = i+step/2-1;
if(mid+1<N){
merge(i, mid, mid+1, min(i+step-1, maxN-1));
}
}
}
}
```
• Heap sort
```const int maxN = 100010;
int arr[maxN+1];
int i = low;
int j = 2*i+1;
while(j <= high){
if(j+1 <= high && arr[j+1] > arr[j]){
j+=1;
}
if(arr[i] < arr[j]){
swap(arr[i], arr[j]);
i = j;
j = 2*i+1;
}else{
break;
}
}
}
void createHeap(){
for(int i=maxN/2; i>=1; i--){
}
}
void sortHeap(){
createHeap();
for(int i=N; i>=1; i--){
swap(arr[i], arr);
}
}
```
• Table sorting

Table sorting is A method, for example, the heavyweight array A[] = {book1, book2, book3}. Each book contains 10GB of content. If you sort directly, moving books in memory will be quite slow. At this time, out of table sorting will be A good choice. Store address index 0 of book1 in B, address index 1 of book2 in B, and address index 2 of book3 in B. When sorting B, you need to read the data of A through the index.
1067 Sort with Swap(0, i)

Table sorting

1067 Sort with Swap parsing

## Strategy two fork tree

### Determination method

A binary tree is an ordered tree in which the degree of nodes is no more than 2. There are only four ways to determine a binary tree:

• The middle order sequence of a binary tree is known, and all values of the sequence are different. There are either pre order sequence or post order sequence, i.e. middle order + pre order. Middle order + post order can uniquely determine the structure of a binary tree.
• The relationship between each node and its child nodes is known.
• It is known that this tree is a complete binary tree and all node values are known.
• It is known that this tree is an AVL tree and its insertion sequence is known.

The way to determine a binary tree is not unique:

• The pre - and post order sequences are known.

The key to determine a binary tree is

1. Whether the root node can be determined.
2. Can you determine which nodes the left and right subtrees are made of.
3. Whether the method of root node and left and right subtrees can be recursive to subtrees.

How can these key factors of building a binary tree be bound to the clues provided by the questions? You can see if there is a relationship between the following table. Note: pre [], middle in [], and post []. The start index and end index of the sequence are preLeft and preRight. The start index and end index of the sequence are inLeft and inRight. The start and end indexes of the sequence are postLeft and postRight. The start and end indexes of a complete binary tree are CBTLeft and CBTRight.

clue Root node Left subtree Right subtree
First order + middle order (1138 Postorder Traversal) pre[preLeft] Find the middle order sequence index rootIdx with the same value as pre[preLeft], left subtree construction node: inLeft~rootIdx Find the middle order sequence index rootIdx with the same value as pre[preLeft], right subtree construction node: rootIdx + 1 ~ inRight
1020 Tree Traversals post[postRight] Find the middle order sequence index rootIdx with the same value as post[postRight], left subtree construction node: inLeft~rootIdx Find the middle order sequence index rootIdx with the same value as post[postRight], right subtree construction node: rootIdx + 1 ~ inRight
Parent node index: left child index, right child index The node with degree 0 is the root node of the whole tree According to the parent-child relationship, we can get the left child tree Right subtree can be obtained according to parent-child relationship
Stack in and stack out sequence: the stack in sequence is the first sequence, and the stack out sequence is the middle sequence (1086 Tree Traversals Again) Same as first order + middle order Same as first order + middle order Same as first order + middle order
Complete binary search tree Construction tree node: N = GBTRight-GBTLeft+1, construction tree layer: L = log(N+1)/log(2), leaf node: leave = N - (pow(2, L) - 1), root node: root = GBTLeft+(pow(2, L-1)-1)+min(leave, (int)pow(2, L-1)) The left subtree is GBTLeft~root-1 The right subtree is root+1~GBTRight

### Storage mode

Binary tree can be stored in two ways:

Linked list storage is a classic way to construct a general binary tree. As long as it is a binary tree, it can be stored in the way of linked list.

```struct Node{
int data;
Node* lchild;//Left subtree
Node* rchild;//Right subtree
}
Node* createNode(int d){ //Create node
Node nd = new Node;
nd->lchild = NULL;
nd->rchild = NULL;
nd->data = data;
return nd;
}
```
• array

Array type storage is used for completely binary tree level traversal records. Root is the index of the root in the array, 2*root is the index of the left child in the array, and 2*root+1 is the index of the right child in the array. Note: the index of the start node of this array is 1.

### Subdivision type

If the binary tree continues to subdivide, it can be divided into complete binary tree, balanced binary tree and red black tree. Some of its characteristics can be used as the basis for its judgment.

Special tree characteristic
1110 Complete Binary Tree When traversing the tree hierarchically, for each node label, starting from root=1, the left child label is 2*root, and the right child label is 2*root+1, then the label of the last node must be equal to the total number of nodes constituting the tree
1066 Root of AVL Tree Definition: balance coefficient = the height of the left subtree - the height of the right subtree. If the balance coefficient of the root node is 2 after adding a node, and the balance coefficient of the left subtree is 1, it needs to rotate to the right. If the balance coefficient of the left subtree is - 1, it needs to rotate the left subtree to the left first, and then rotate the root node to the right. If the balance coefficient of the root node is - 2 and the balance coefficient of its right subtree is - 1, then the root node needs to rotate left. If the balance coefficient of its right subtree is 1, then the right subtree needs to rotate right first and then the root node needs to rotate left. It can be simply recorded as: if LL, then R-SPIN; if LR, then LR spin; if RR, then L-spin; if RL, then RL spin
1135 is it a red black tree The root node must be black. If a node is red, its child nodes are black. Starting from a node, the number of black nodes on the path to all its children is the same.

## DFS, BFS

DFS can also be said that it does not hit the south wall and does not look back. There is a standard template, just set the template directly.

```void dfs(int next step){
if(Hit the south wall)
return;//come back
dfs(next step+1);
}
int main(){
dfs(Step 1);
}
```

BFS is also called the queuing method. There are standard templates. Let's set them together.

```void bfs(int s){
queue<int> q;
q.push(s);
while(!q.empty()){
Take out the team head node top;
Visit the team head node top;
Remove the first element from the team;
All the nodes in the next level of top that have not yet joined the team will join the team and be set as joined;
}
}
```

DFS and BFS exist as tools to explore relationships. With these two tools, we can clarify the relationships and structures.

Relationship category function
Binary tree Traverse the nodes to get the hierarchical traversal sequence. The only necessary condition for determining binary tree + ergodic construction of binary tree
Connection diagram Judge whether it is in the same connection diagram: if it starts from one node, it can traverse all nodes (1013 Battle Over Cities)
chart Traverse all nodes. Judging Euler, semi Euler, semi Euler, Hamiltonian cycle, 1150 traveling salesman problem, Djkstra's shortest path assisted judgment

DFS and BFS can be used for mutual conversion, but there are still some differences to be noted when using them.
The function parameters of DFS can pass hierarchy, next state and recursive information. If you need to traverse a binary tree with DFS, you want to get its height. Then the height information can be passed down as a parameter of the function.

```struct Node{
int data;
Node* lchild;
Node* rchild;
}
void dfs(Node* root, int height){
if(root==NULL){
return;
}
dfs(root->lchild, height+1);
dfs(root->rchild, height+1);
}
```

If BFS is needed, it is different from the parameters of recursive function. At this time, nodes and structures can be used to transmit information.

```struct Node{
int data;
Node* lchild;
Node* rchild;
int level;//arrangement
}
void bfs(Node* root){
queue<Node*> q;
root->level = 1;
q.push(root);
while(!q.empty()){
Node* nd = q.front();
q.pop();
if(nd->lchild!=NULL){
nd->lchild->level = nd->level+1;
q.push(nd->lchild);
}
if(nd->rchild!=NULL){
nd->rchild->level = nd->level+1;
q.push(nd->rchild);
}
}
}
```

## Combination of strategies, topological structure

And look-up set and topology structure are also the structures that describe the relationship between multiple nodes, and the function of look-up set is to gather the nodes that are related together. The function of topological structure is to develop, to show the sequence of each node, and to judge whether a given graph is a directed acyclic graph.
Check the template code corresponding to 1114 Family Property, 1118 Birds in Forest

```const int maxN = 10010;
int father[maxN];
void initFather(){
for(int i=0; i<maxN; i++){
father[i] = i;
}
}
int findFather(int x){
if(father[x]==x){
return father[x];
}else{
int F = findFather(father[x]);
father[x] = F;
return F;
}
}
void unionGroup(int x, int y){
if(findFather(x)!=findFather(y)){
father[findFather(x)] = findFather(y);
}
}
```

Code template for topological order

```const int maxN = 10010;
vector<int> G[maxN];
int n, m, inDegree[maxN];
bool topologicalSort(){
int num = 0;
queue<int> q;
for(int i=0; i<n; i++){
if(inDegree[i]==0){
q.push(i);
}
}
while(!q.empty()){
int u = q.front();
q.pop();
for(int i=0; i<G[u].size(); i++){
int v = G[u][i];
inDegree[v]--;
if(inDegree[v] == 0){
q.push(v);
}
}
G[u].clear();
num++;
}
if(num == n) return true;
else return false;
}
```

## Dijkstra shortest path

Dijkstra is used to solve the shortest path problem of single source, that is, given graph G and starting point S, the shortest distance from S to every other vertex is obtained by algorithm. Generally, Dijkstra scenarios need to be used with DFS to solve problems. Dijkstra can find the shortest path and the corresponding set of pre path nodes. Generally, there is not only one shortest path. In this case, other conditions are needed to select the most suitable one from these same shortest paths, and DFS is needed.
There are two ways to construct graph structure, one is adjacency matrix and the other is adjacency table. Because adjacency matrix needs to define two-dimensional array directly, if the number of nodes is not many, adjacency matrix can be used directly. If the number of nodes is too many, it is better to define graph by adjacency table.

```//adjacency matrix
G[N][N];
//Adjacency table for storing terminal number and edge weight
struct Node{
int v; //End number of edge
int w; //Border right
}
```

Dijkstra shortest path + DFS (1018 Public Bike Management) template code

```const int maxN = 100;
const int INF = 99999999;
int G[maxN][maxN];
bool visited[maxN];
int d[maxN];
int start; //start
int dest; //termination
int N; //Total points
vector<int> pre[maxN];
vector<int> temppath;
vector<int> path;
void dfs(int start){
temppath.push_back(start);
if(start==dest){
for(int i=temppath.size()-1; i>=0; i--){
//According to the processing content of each path
}
temppath.pop_back();
return;
}
for(int i=0; i<pre[start].size(); i++){
dfs(pre[start][i]);
}
temppath.pop_back();
}
int main(){
fill(d, d+N, INF);//initialization
d[start]=0;
for(int i=0; i<N; i++){
int mind=INF;
int u=-1;
for(int j=0; j<N; j++){
if(!visited[j] && d[j]<mind){
mind=d[j];
u=j;
}
}
if(u==-1){
break;
}
visited[u]=true;
for(int j=0; j<N; j++){
if(!visited[j] && G[u][j]!=INF){
if(d[u]+G[u][j]<d[j]){
d[j]=d[u]+G[u][j];
pre[j].clear();
pre[j].push_back(u);
}else if(d[u]+G[u][j]==d[j]){
pre[j].push_back(u);
}
}
}
}
dfs(start);
}
```

## Big number operation of strategy

The condition for judging whether it is a large number operation is to see whether the range of input numbers prompted in the title is greater than 10 ^ 18. There are two kinds of big number operations, one is the operation of integer type big number, the other is the operation of fractional type big number.
The processing method of integer large number is to read data in the form of character array and convert it to integer array, and then calculate each bit of the number respectively.

```struct bign{
int d;
int len;
bign(){
memset(d, 0, sizeof(d));
len=0;
}
}
/**
Convert the read in character array to a large number structure
**/
bign change(char str[]){
bign a;
a.len = strlen(str);
for(int i=0; i<a.len; i++){
a.d[i] = str[a.len-i-1] - '0';
}
return a;
}
/**
**/
bign c;
int carry=0;//carry
for(int i=0; i<a.len||i<b.len; i++){
int temp = a.d[i]+b.d[i]+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
if(carry!=0){
c.d[c.len++] = carry;
}
}
/**
Subtraction of large numbers
**/
bign sub(bign a, bign b){
bign c;
for(int i=0; i<a.len || i<b.len; i++){
if(a.d[i]<b.d[i]){
a.d[i+1]--;
a.d[i]+=10;
}
c.d[c.len++] = a.d[i]-b.d[i];
}
//Remove the 0 of the high position and keep at least one lowest position
while(c.len-1>=1 && c.d[c.len-1] == 0){
c.len--;
}
return c;
}
/**
Multiplication of large numbers
**/
bign multi(bign a, bign b){
bign c;
int carry = 0;
for(int i=0; i<a.len; i++){
int temp = a.d[i]*b+carry;
c.d[c.len++]=temp%10;
carry=temp/10;
}
while(carry!=0){//Multiplication has more than one carry
c.d[c.len++]=carry%10;
carry/=10;
}
return c;
}
/**
Division of large numbers
**/
bign divide(bign a, bign b, int& r){
bign c;
c.len = a.len;
for	(int i=a.len-1; i>=0; i--){
r = r*10+a.d[i];
if(r<b){
c.d[i]=0;
}else{
c.d[i] = r/b;
r = r%b;
}
}
while(c.len-1>=1 && c.d[c.len-1]==0){
c.len--;
}
return c;
}
```

Fractional large numbers (1088 Rational Arithmetic) can be treated as molecules and denominators respectively.

```struct Fraction{
int up, down;
};
/**
greatest common factor
**/
int gcd(int a, int b){
if(b==0){
return a;
}
return gcd(b, a%b);
}
/**
Fractional simplification
**/
Fraction reduction(Fraction result){
if(result.down<0){
result.up = -result.up;
result.down = -result.down;
}
if(result.up==0){
result.down = 1;
}else{
int d = gcd(abs(result.up), abs(result.down));
result.up/=d;
result.down/=d;
}
return result;
}
/**
**/
Fraction result;
result.up = f1.up*f2.down+f2.up*f1.down;
result.down = f1.down*f2.down;
return reduction(result);
}
/**
fraction subtraction
**/
Fraction minu(Fraction f1, Fraction f2){
Fraction result;
result.up = f1.up*f2.down-f2.up*f1.down;
result.down = f1.down*f2.down;
return reduction(result);
}
/**
Fractional multiplication
**/
Fraction multi(Fraction f1, Fraction f2){
Fraction result;
result.up = f1.up*f2.up;
result.down = f1.down*f2.down;
return reduction(result);
}
/**
Fractional division
**/
Fraction divide(Fraction f1, Fraction f2){
Fraction result;
result.up = f1.up*f2.down;
result.down = f1.down*f2.up;
return reduction(result);
}
```

## String processing of strategy

### Construction method

There are many ways to construct string, as shown below.

```string str;
cin >> str;
```
• Read in through char [] and construct string
```char c;
scanf("%s", c);
string str = string(c);
```

### traversal method

Traversal methods are introduced as follows.

• Index traversal
```string str = "abc";
for(int i=0; i<str.size(); i++){
printf("%c", str[i]);
}
```
• Iterator traversal
```string str = "abc";
for(string::iterator it=str.begin(); it!=str.end(); it++){
printf("%c", *it);
}
```

### Delete element

There are two ways to delete elements.

• Iterator delete
```string str = "abcdefg";
str.erase(str.begin()+2, str.end()-1);
cout << str << endl;
```

Output results:

```abg
```
• Index delete
```string str = "abcdefg";
str.erase(3, 2);
cout << str << endl;
```

Output results:

```abcfg
```

### Search method

Search is divided into a search string or a character.

```string str = "abcdeft";
int cPosition = str.find_first_of('c');
```
```string str = "abcdefg";
int idx = str.find("cde");
int idx2 = str.find("cde", 2);//Matching cde substring from 2 positions
```

### Number and string conversion

This transformation is usually used in simple large number computing scenarios.

```//string to int
string str = "111";
int a = stoi(str);
//int to string
str = to_string(a);
//char [] to int
string str = "111";
char c = "111";
int b = atoi(c);
b = atoi(str.c_str());
```

### toggle case

If you need to change the whole string to uppercase or lowercase, you can do the following.

```string str = "abcABC";
//Convert to uppercase
transform(str.begin(), str.end(), str.begin(), ::toupper);
//Convert lowercase
transform(str.begin(), str.end(), str.begin(), ::tolower);
```

## Array of strategies

Array is the basic container for storing data. Here are three types of data that can be hosted by this container.

data type Brief description Question type
number The index of an array is unique. An object that can be marked with an ID is also unique. The uniqueness of an object corresponds to the uniqueness of an index one by one. The data stored in the corresponding index array can reflect the number of objects 1048 Find Coins: the denomination of coins is unique and can be used as an index of the array. The number of coins with different denominations can be stored in the index of the corresponding array. 1054 the dominiant color: different colors are unique and can be used as the index of the array. The times of different colors can be stored under the index of the corresponding array. 1057 Stack: there is a upper limit for the number provided by the title. The number corresponds to the array index one by one, and the number of times the number appears is stored in the corresponding array index. If there are n numbers, the middle number is the N/2 number. It is easy to find the sum by using the tree array. 1092 To Buy or Not to Buy: this question is the same as 1048 Find Coins, except that [0-9,a-z,A-Z] needs to be converted to 0-36 in preparation
Sign in Array index uniqueness can represent a unique object. The value under the array index corresponds to whether the object appears The 1121 Damn Single array index indicates the ID of the person. If this person A appears, the check-in array will be updated. The value under the check-in index of the ID of this person's couple B will be updated to checked in. When traversing the person of couple B, if the lower value of check-in is arrived, it means that couple A has arrived. The index of 1149 Dangerous Goods Packaging array corresponds to the ID of dangerous goods one by one. The value of the array is whether dangerous goods appear or not.
memory The index of an array is monotonically increasing, which is the same as the extension of the time axis. The state of IDX of an array can be regarded as the current state, idx-1 as the past state, and idx+1 as the future state The value under each index of the 1093 Count PAT's array indicates how many times P has occurred in this location, which is only related to the number of P corresponding to the previous location. 1101 Quick Sort traverses from left to right, array leftMax records the maximum value of the corresponding position, which is related to the maximum value of the previous position, traverses from right to left, array rightMin records the minimum value of the corresponding position, which is related to the minimum value of the previous position.

Tree array summing template:

```#define lowbit(i) ((i) & (-i))
const int maxN = 100010;
int c[maxN];
int getSum(int x){
int sum = 0;
for(int i=x; i>=1; i-=lowbit(i)){
sum += c[i];
}
return sum;
}
void update(int x, int v){
for(int i=x; i<maxN; i+=lowbit(i)){
c[i]+=v;
}
}
```

# Summary of the strategies for solving regular problems

This is the end of the analysis of the strategy to solve the law problem. Presumably, all players have already seen everything. The reason why the law problem contains the word "law" is that when the designer designs the problem, there are upper and lower bounds of the design, and there are reference frames. As long as the framework is mastered, it can be broken by direct application.
To be continue...

Tags: less Programming

Posted on Fri, 26 Jun 2020 00:12:19 -0400 by cdog5000