Introduction to Niuke algorithm competition notes 1

Keywords: Algorithm Dynamic Programming

2021-10-20: I opened a new pit yesterday. I felt OK after watching the previous episodes. I regretted why I didn't follow it earlier. I felt that my knowledge system was too scattered before. It seemed that the class ended in November or December. She said that she could reach the level of icpc bronze medal. I believe it for the time being. I hope I can make some progress after learning. I don't want a bronze medal. cf can go to 1500 first, woo woo woo.

#Simulation, enumeration and greed

String (nowcoder.com)

 

Ruler taking method (to be honest, this may be the first time I have seen this method, or I know its scientific name). The normal idea of violence should be to enumerate two endpoints, but the right endpoint here doesn't need to go back. Just think about it. Therefore, as long as the two endpoints move to the right at a differential speed, it is the complexity of O(n)

 #include <bits/stdc++.h>
 using namespace std;
 int main(){
     map<char,int>x;
     string a;
     cin>>a;
     int l=0,r=0;
     for(int i=0;i<a.size();i++){
         x[a[i]]++;
         if(x.size()==26){
             r=i;
             break;
         }
     }
     int ans=r-l+1;
     for(int i=0;i<a.size();){       
         if(r!=a.size()-1){
             r++;
             x[a[r]]++;
         }   
         while(x[a[i]]>1){
             x[a[i]]--;
             i++;
         }
         if(x.size()==26){
             ans=min(ans,r-i+1);
         }
         if(r==a.size()-1){
             break;
         }       
     }
     cout<<ans<<endl;
     return 0;
 }

Throw a handkerchief (nowcoder.com)

The ruler took + 1, but I made my first serve wa, 90 points, because each movement should consider not only the maximum value counterclockwise, but also the maximum value clockwise.

 #include <bits/stdc++.h>
 using namespace std;
 #define ll long long
 ll x[100001];
 int main(){
     int n;
     cin>>n;
     ll sum=0;
     for(int i=0;i<n;i++){
         cin>>x[i];
         sum+=x[i];
     }
     ll l=0,r=0;
     ll now=0,ans=0;
     while(1){
         while(now*2<=sum&&r<=n-1){
             ans=max(now,ans);
             now+=x[r];
             r++;
         }
         while(now*2>sum){
             ans=max(ans,sum-now);//The first one didn't add this sentence
             now-=x[l];
             l++;
         }
         if(r==n){
             break;
         }
     }
     cout<<ans<<endl;
     return 0;
 }

Flip Game (nowcoder.com)

Black and white chess question:

1 - each piece can only be turned once at most

2 - we enumerate the translations of the first line (16 kinds), and then pass out the translations of the second and third lines (if the previous line does not turn over the second line, turn over the lower one). If the fourth line is turned over, it will succeed, otherwise it will fail

There is a bit operation method in the problem solution. Every line is regarded as a binary string. If I press the first and third, the corresponding binary code by operation is 1010. Now I have to reverse three places: first, reverse the standard, directly make the original code exclusive or with the operation code, and second: reverse each line by the left of the operation, move the operation code left by one bit and then exclusive or, Third: reverse the right side of each operation, shift the operation code to the right by one bit, and then XOR.

XOR is a binary addition without carry (easy to remember)

The teacher adjusted the course for a long time, but I saw at a glance that she had a problem, so I was in a hurry ()

 #include <iostream>
 #include <cstdio>
 #define INF 0x3f3f3f3f
 using namespace std;
 ​
 int a[5],b[5];
 int ans=INF;
 ​
 int count_1_num(int x){
     int num=0;
     while(x>=1){
         num+=x%2;
         x/=2;
     }
     return num;
 }
 ​
 int fun(int*x){
     int t[5];
     //Enumerate 16 cases of the first line
     for(int i=0;i<16;i++){
         //If there are several 1s in the calculation, it means that it has been flipped several times
         int num=count_1_num(i);
         for(int i=0;i<4;i++){
             t[i]=x[i];
         }
         t[0]=(t[0]^(i)^(i<<1)^(i>>1));
         t[1]=(t[1]^(i));
         t[0]=(t[0]&15);
         for(int k=1;k<4;k++){
             num+=count_1_num(t[k-1]);
             t[k]=(t[k]^(t[k-1])^(t[k-1]<<1)^(t[k-1]>>1));
             t[k+1]=(t[k+1]^(t[k-1]));
             //Change the fifth bit that may overflow to 0 (this step is beyond my expectation) 
             t[k]=t[k]&15;       
         }
         if(t[3]==0){
             ans=min(num,ans);
         } 
     }
 }
 ​
 int main(){
     for(int i=0;i<4;i++){
         for(int k=0;k<4;k++){
             char c;
             scanf(" %c",&c);
             if(c=='b'){
                 //Change the k+1 bit of a[i] to 1 
                 a[i]=a[i] | (1<<k); 
             }else{
                 b[i]=b[i] | (1<<k);
             }
         }
     }
     fun(a);
     fun(b); 
     //I didn't expect that I forgot to add 0 point for the first shot. Can I say...
     if(ans==INF){
         cout<<"Impossible"<<endl;
         return 0;
     }
     cout<<ans<<endl;
     return 0;
 }



 

 

It shocked me for a whole year. The data range of the original question is very small. You can add and subtract all student numbers, but if the data range is very large and the sum of student numbers has exceeded long long, you can't do so

So, it comes, bit operation: the principle of addition, subtraction and elimination and the principle of twice exclusive or invariance are interconnected, so we can XOR all student numbers, and then XOR the student numbers of people who fall, and the rest are those who don't come.

Shit, it's so handsome. I directly worship the man-made teacher who invented this algorithm, orz

Upgrade again. What if two people don't come?

First, operate the same as above to get the student number XOR value of two people who didn't come. Find one with 1, indicating that the two people are different in this bit. You can divide the original number into two pieces. Each piece is a problem that one person didn't come.

Ohhh, 130 no white flowers, long experience.

Problem - 333E - Codeforces

 

This question seems nothing to him. Search 2500. Mom, what am I learning? I deserve to think of 2500 as a person with 1300 points?

The idea is very clever. We enumerate the distance between two points and sort them from large to small, from long side to short side. Half of the shortest side length of the first triangle is the answer

But. How can we judge when to form a triangle? The most simple idea is that I record the verified adjacent points corresponding to each point every time I walk through an edge. If there is an edge AC,A has passed B, and c has passed B, it will become. But if the normal recording traversal is bound to timeout, what should I do? The idea that shocked me for two years came. There is a bitset class in c + +, which is a very long binary string. When we save the passed points, we convert it into the value of the corresponding bitset bit, and then judge the bitset bit and of the two endpoints when each new edge comes. If it is not zero, it means that they have passed the same point before and live together!

I actually wrote it myself. Shit, cow, go and fix the bitset interface (in the fragmented document)

#include <bits/stdc++.h>
 using namespace std;
 pair<double,double>x[3001];
 struct node{
     int x,y;
     double dis;
 };
 node t[10000001];
 bitset<3001>ans[3001];
 ​
 double distance(pair<double,double>x,pair<double,double>y){
     return sqrt((x.first-y.first)*(x.first-y.first)+(x.second-y.second)*(x.second-y.second));
 }
 ​
 bool com(node a,node b){
     return a.dis>b.dis;
 }
 ​
 ​
 int main(){
     int n;
     cin>>n;
     ans[0].reset();
     for(int i=1;i<=n;i++){
         cin>>x[i].first>>x[i].second;
         ans[i].reset();
     }
     int num=0;
     for(int i=1;i<=n;i++){
         for(int j=i+1;j<=n;j++){
             t[num].x=i;
             t[num].y=j;
             t[num].dis=distance(x[i],x[j]);
             num++;
         }
     }
     sort(t,t+num,com);
     for(int i=0;i<num;i++){
         int x=t[i].x;
         int y=t[i].y;
         if((ans[x]&ans[y])!=0){
             printf("%.8lf",t[i].dis/2);
             break;
         }
         ans[x].set(y,1);
         ans[y].set(x,1);
     
     }
     return 0;
 }

Monthly chahuahua's mobile phone (nowcoder.com)

Question meaning: judge whether sequence b is a subsequence of a (discontinuous)

When I saw this question, I actually wanted to go to kmp. Shit, absolutely. kmp is a matching continuous substring. This is a discontinuous sequence, which is actually a double pointer. However, the data range of this question is very large, so if I use a double pointer for each target string, it's t.

As like as two peas, the first place that appears in every letter is swept away from the front, and it feels like the dp entry of ccpc string.

 #include <bits/stdc++.h>
 using namespace std;
 ​
 int x[1000001][30];
 int last[30];
 ​
 int main(){
     string a;
     cin>>a;
     memset(last,-1,sizeof(last));
     for(int i=a.size()-1;i>=0;i--){
         for(int k=0;k<26;k++){
             x[i][k]=last[k];
         }
         last[a[i]-'a']=i;
     }
     int t;
     cin>>t;
     while(t--){
         string b;
         cin>>b;
         int pos=last[b[0]-'a'];
         int beg=pos;
         if(pos<0){
             cout<<"No"<<endl;
             continue;
         }
         int flag=1; 
         for(int i=1;i<b.size();i++){
             pos=x[pos][b[i]-'a'];
             if(pos<0){
 //              pos=x[beg][b[0]-'a'];// This is because I don't want to understand the string and sequence and want to go back
 //              beg=pos;
                 if(pos<0){
                     flag=0;
                     cout<<"No"<<endl;
                     break;
                 }
                 //i=0;
             }
         }
         if(flag==1){
             cout<<"Yes"<<endl;
         }
     }
     return 0;
 } 

Problem - 484A - Codeforces

Find the number with the largest number of binary 1 in l-r

Bit operation + greedy. Because the data range is too large, it is impossible to traverse. Therefore, consider greedy, change the left and right boundaries into binary, and change the binary bits with the left boundary of 0 to 1 one by one to see if they are still less than the right boundary. tql, I can't think of it myself, but I wrote the implementation myself. I know a little bit about bit operation(

 #include <bits/stdc++.h>
 using namespace std;
 #define ll long long
 int main(){
     int t;
     cin>>t;
     while(t--){
         ll l,r;
         cin>>l>>r;
         ll ans=l;
         for(ll i=1;i<=r;i<<=1){
             if((l&i)==0){
                 ans+=i;
                 if(ans>r){
                     ans-=i;
                     break;
                 }
             }
         }
         cout<<ans<<endl;
     }   
     return 0;
 }

Rabbit's interval password (nowcoder.com)

Find the maximum value of the exclusive or of two numbers in l-r

Find the first different binary bit of L and R. from this, you can ensure that one number is all 1 and the other is all 0

 #include <bits/stdc++.h>
 using namespace std;
 #define ll long long
 int main(){
     int t;
     scanf("%d",&t);
     while(t--){
         ll a,b;
         scanf("%lld %lld",&a,&b);
         int i=63;
         for(;i>=0;i--){
             if((a>>i)!=(b>>i)){
                 break;
             }
         }
         printf("%lld\n",(1LL<<i+1)-1);
     }
     return 0;
 }

Bit operation: consider bit by bit

Recursion and divide and conquer

Expression evaluation 4 (nowcoder.com)

The meaning of the question is the same as that of the title. At that time, a similar operation was arranged for the data structure. The idea is to find the symbol with the lowest operation level every time, and divide the expression into two parts for calculation respectively.

 #include <bits/stdc++.h>
 using namespace std;
 string a;
 //string to int 
 int num(int l,int r){
     int ans=0;
     for(int i=l;i<=r;i++){
         ans*=10;
         ans+=a[i]-'0';
     }
     return ans;
 }
 //Formula for calculating l to r 
 int calc(int l,int r){
     int pos1=-1,pos2=-1,pos3=-1;
     int cnt=0;
     //Find the position of the operator with the lowest priority that is not in parentheses 
     for(int i=l;i<=r;i++){
         if(a[i]=='('){
             cnt++;
         }
         if(a[i]==')'){
             cnt--;
         }
         if(cnt==0){
             if(a[i]=='+'||a[i]=='-'){
                 pos1=i;
             }
             if(a[i]=='*'||a[i]=='/'){
                 pos2=i;
             }
             if(a[i]=='^'){
                 pos3=i;
             }
         }       
     }
     //If the operator is not found 
     if(pos1==-1&&pos2==-1&&pos3==-1){
         //Or it's surrounded by parentheses as a whole 
         //Note: the last two else if are to exclude redundant parentheses 
         if(cnt==0&&a[l]=='('){
             return calc(l+1,r-1);
         }else if(cnt<0&&a[l]=='('){
             return calc(l,r-1);
         }else if(cnt>0&&a[r]==')'){
             return calc(l+1,r);
         }
         //Or it's a number 
         return num(l,r);
     }
     //+- lowest priority, judge first 
     if(pos1!=-1){
         if(a[pos1]=='+'){
             return calc(l,pos1-1)+calc(pos1+1,r);
         }else{
             return calc(l,pos1-1)-calc(pos1+1,r);
         }
     }   
     //And then */ 
     if(pos2!=-1){
         if(a[pos2]=='*'){
             return calc(l,pos2-1)*calc(pos2+1,r);
         }else{
             return calc(l,pos2-1)/calc(pos2+1,r);
         }
     }
     //Finally, power
     if(pos3!=-1){
         return pow(calc(l,pos3-1),calc(pos3+1,r)); 
     }
 }
 ​
 int main(){
     cin>>a;
     cout<<calc(0,a.size()-1)<<endl;
     return 0;
 }

1027 Kotori and candy _2021autumn algorithm introductory class Chapter 2 exercise: recursion, divide and conquer (nowcoder.com)

A total of n piles of candy, one for each pile. Merge the two piles with different quantities. 1. Find the minimum total cost

This question was originally a very simple memory search, but I didn't think clearly what to return. I wanted to return the length of both ends, but then I thought, no, with n, don't you have the length of both ends, so what should be returned is the operands required to form these two ends.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll,ll> mp;
ll getans(ll n){
    if(n==0) return 0;
    if(mp.count(n)) return mp[n];
    if(n&1){
        mp[n]=2*getans(n/2);
    }else{
        mp[n]=getans(n/2)+getans(n/2-1)+1;
    }
    return mp[n];
}
int main(){
    ll t;
    scanf("%lld",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        if(n==0)puts("0");
        else printf("%lld\n",getans(n-1));
    }
}

Two point, three point, 01 point planning

Shit, I remember I finished writing this part, but it seems that I forgot to save it before turning off the computer, and then it disappeared. My mind exploded. Write it again, woo woo

Xiaomi shopping (nowcoder.com)

For n items, each item has a value and cost. Find the ratio of the maximum total value (val) to the total cost (cos) of k items

Idea: assuming that the maximum ratio is x, there must be such a scheme for the ratio smaller than x, and there must be no such scheme for the ratio larger than x, then we can consider dichotomy. For a ratio num, if it is feasible, there must be a scheme so that Val / cos > = num -- > val numcos > = 0; Therefore, when we judge, we count all the maximum k values of Val numcast. If this doesn't work, it really doesn't work.

#include <bits/stdc++.h>
using namespace std;
#define ll long long
struct node{
	ll c,v,mar;
};
ll n,m;
node x[10001];
bool com(node a,node b){
	return a.mar>b.mar;
}
bool fun(ll a){
	ll num=0;
	for(ll i=0;i<n;i++){
		x[i].mar=x[i].v-a*x[i].c;
	}
	sort(x,x+n,com);
	ll sum=0;
	for(ll i=0;i<m;i++){
		sum+=x[i].mar;
	}	
	if(sum<0){
		return false;
	}else{
		return true;
	}
}

int main(){
	ll t;
	cin>>t;
	while(t--){		
		cin>>n>>m;
		for(ll i=0;i<n;i++){
			cin>>x[i].c>>x[i].v;
		}
		ll l=0,r=1e8+1;
		ll mid=0;
		while(l<r){
			mid=(l+r+1)/2;
			if(fun(mid)){
				l=mid;
			}else{
				r=mid-1;
			}
		}
		cout<<l<<endl;
	}
	return 0;
}

gpa (nowcoder.com)

It's similar to the previous question, that is, we change the maximum formula, and we do the same

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ll double
struct node{
	double a,b,tem;
};
node x[100001];

int n,m;

bool com(node a,node b){
	return a.tem<b.tem;
}

bool fun(ll a){
	for(int i=0;i<n;i++){
		x[i].tem=x[i].a*(x[i].b-a);
	}
	sort(x,x+n,com);
	for(int i=0;i<m;i++){
		if(x[i].tem<0){
			x[i].tem=-INF;
		}
	}
	ll sum=0;
	for(int i=0;i<n;i++){
		if(x[i].tem==-INF){
			continue;
		}
		sum+=x[i].tem;
	}
	if(sum<0){
		return false;
	}else{
		return true;
	}
}

int main(){	
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>x[i].a;
	}
	for(int i=0;i<n;i++){
		cin>>x[i].b;
	}
	double l=0,r=1e11+1;
	while(r-l>1e-6){//The answer here is decimal, so we use the method of controlling accuracy
		double mid=(l+r)/2;
		if(fun(mid)){
			l=mid;
		}else{
			r=mid;
		}
	}
	printf("%.6lf\n",l);
	return 0;
}

Stack, queue, monotone stack, monotone queue

[NOIP2016] earthworms (nowcoder.com)

Question meaning: give you n numbers, one number will be divided into two numbers with a fixed proportion each time. Find out how to make each number as small as possible for m times of division, and each number will increase by a certain value over time.

I thought it was easy to solve this problem. Priority queue, who wouldn't (), but after looking at the data range, it's over. I can't help it. I can only listen to Yuju.

The idea is really ingenious. Suppose we sort the initial sequence and put the two segments into two queues respectively after splitting, because each time we split the two segments must be smaller (or the same) than the last time, so the two segments after splitting must be smaller than the previous two segments, so the ones we put in the queue later must be smaller than the ones we put in first, so as to ensure order, Each time you select the maximum, just look for the heads of three queues. tql, an unimaginable path.

But this output requirement is disgusting. I finished it very hard. What do you mean by having to torture me on the output

#include <bits/stdc++.h>
#include <vector>
#include <queue>
using namespace std;
#define ll long long

int main() {
	queue<int>q[3];
	int x[100001];
	ll n, m, Q, u, v, t;
	cin >> n >> m >> Q >> u >> v >> t;
	double p = (double)u / v;
	for (int i = 0; i < n; i++) {
		cin >> x[i];
	}
	sort(x, x + n, greater<ll>());
	for (int i = 0; i < n; i++) {
		q[0].push(x[i]);
	}
	int now = 0;
	while (now < m) {
		int mn = -0x3f3f3f3f, maxindex=-1;
		for (int i = 0; i < 3; i++) {
			if (!q[i].empty() && q[i].front() > mn) {
				mn = q[i].front();
				maxindex = i;
			}
		}
		if (maxindex==-1) {
			break;
		}
		mn += now * Q;
		q[maxindex].pop();
		q[1].push((int)(u * mn/v) - now * Q-Q);
		q[2].push(mn - (int)(u * mn/v) - now * Q-Q);
		
		if ((now+1) % t == 0) {
			cout << mn << " ";
		}
		now++;
	}
	cout << endl;
	now=1;
	while (!q[0].empty() || !q[1].empty() || !q[2].empty()) {
		int mn = -0x3f3f3f3f, maxindex=-1;
		for (int i = 0; i < 3; i++) {
			if (!q[i].empty() && q[i].front() > mn) {
				mn = q[i].front();
				maxindex = i;
			}
		}
		if (maxindex==-1) {
			break;
		}
		if(now%t==0){
			cout << mn+m*Q << " ";
		}	
		now++;
		q[maxindex].pop();
	}
	return 0;
}

Sliding Window (nowcoder.com)

 

Dynamically maintain a queue that may be the answer between i-k~i.

Take the maximum value as an example. If the latter one is larger than the former one, the former one will lose the opportunity to become the answer and can leave the team. Otherwise, it will not leave the team. However, when the position of this value is smaller than the left end point of the interval, I will also leave the team. I originally wanted to use the queue to save data, but the rain woke me up. If I save data, I have to wait until the subscript, So we can save the subscript directly and find the corresponding data in the original array.

The first time I used the opposite beat, it was a little cool. The first time I handed it in, wa it was when the length was 1.

#include <bits/stdc++.h>
using namespace std;
int x[1000011];
int main(){
//	freopen("a.in","r",stdin);
//	freopen("std.out","w",stdout);
	int n,k;
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>x[i];
	}
	if(n==1){
		cout<<x[0]<<endl<<x[0];
		return 0;
	}
	deque<int>q;
//    q.push_back(0); The first time wa is here. Don't tm insert it in advance in the future. It's great when shooting, but it's troublesome to write
	for(int i=0;i<n;i++){
		while(!q.empty()&&x[q.back()]>x[i]){
			q.pop_back();
		}
		q.push_back(i);
		if(q.front()<=i-k){
			q.pop_front();
		}
		if(i+1>=k){
			cout<<x[q.front()]<<" ";
		}	
	}
	cout<<endl;	
	q.clear();
	for(int i=0;i<n;i++){
		while(!q.empty()&&x[q.back()]<x[i]){
			q.pop_back();
		}
		q.push_back(i);
		if(q.front()<=i-k){
			q.pop_front();
		}
		if(i+1>=k){
			cout<<x[q.front()]<<" ";
		}
	}
	return 0;
}

Largest Rectangle in a Histogram (nowcoder.com)

The board problem of monotone stack. The so-called monotone stack is that we maintain a monotone stack. Whenever new data comes in, if it destroys the original monotonicity, we will go out of the stack until it is monotonous.

Give you a string of equal width columns with different heights, and find the largest rectangular area that can be drawn inside.

Idea: we save the maximum length that each column can extend to the left. If the newly added column is higher than the previous one, its maximum width is 1. If the newly added column is lower than the previous one, we add the maximum length that each column extends to the left to get the maximum length of the new column. Of course, If a column comes out of the stack, it means that it can no longer extend to the right. At this time, we use the height × The maximum length it can extend to the left is a possible maximum area.

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int x[100001];
int num[100001];//Record the maximum length that each column can extend to the left
int main(){
	int n;
	while(cin>>n&&n){
		for(int i=1;i<=n;i++){
			cin>>x[i];
		}
		stack<ll>stk;
		ll ans=0;
		x[++n]=0;
		for(int i=1;i<=n+1;i++){
			ll temp=0;
			while(!stk.empty()&&x[i]<=x[stk.top()]){
				temp+=num[stk.top()];
				ans=max(ans,temp*x[stk.top()]);
				stk.pop();
			}
			stk.push(i);
			num[stk.top()]=temp+1;
		}
		cout<<ans<<endl;
	}
	return 0;
} 

Posted by Dia:NL on Wed, 17 Nov 2021 05:10:22 -0800