【NOI】9272 Even number three

topic

Link: bajdcc/ACM

describe

在所有的N位数中,有多少个数中有偶数个数字3?结果模12345。(1<=N<=10000)

sample input

2

Sample output

73

Method 1: Exhaustive

Evaluation: The easiest and least efficient method.

Defect: When N is very large, the i used for traversal cannot be placed in long long, gg. But first, you have to be patient until long long overflows. It doesn't take time, it's too slow.

#include <iostream>

using namespace std;

#define LL long long
#define NUM 3

int main() {
	LL m,n,i,j,t,count;
	cin>>n;
	for (i=0,m=1;i<n;i++) m*=10; // Find the upper bound of N digits 
	for (i=m/10,count=0;i<m;i++) { // From 10..000 ~ 99..999 
		for (j=0,t=i;t;t/=10) // take each bit 
			if (t%10==NUM) j++; // count if NUM j plus one 
		if (j%2==0) {
		    count++; // even NUM count count plus one
		    count%=12345;
		}
	}
	cout<<count;
	return 0;
}

Method 2: Recursion

The exhaustive method has an inherent flaw: the traversal range of i is limited, which can be avoided unless high precision is used.

After further thinking, change the question to "How many numbers have an even number of 4", and the result is recorded as N4. Then I think N4 should be the same as N3, symmetry. Proof: Corresponding to the number of even number 3 in each number, I can find the corresponding number, as long as the 3 and 4 in the original number are adjusted down, for example, 133242, the adjustment becomes 144232, which is eu. Of course, thinking of this conclusion, we can only prove that N1~N9 are equal at present. As it should be, if we know the sum of N1~N9, then we can get the result by averaging. However, if you are still at a loss, think about it with recursion.

If there is currently a number 6XXXXX, how many eligible numbers starting with 6 are there? Well, ignore 6 and get f(6XXXXX)=f(XXXXX), because 6 doesn't need to be included at all, oh! We found an important conclusion: some subproblems are duplicates! So the reason the brainless exhaustive method is so slow is that it computes repeated subproblems. Well, now to find out which sub-problems are duplicates.

Assume the functions f(n) and g(n), where n is the number of digits, f represents the total number of even 3s, and g represents the total number of odd 3s. Starting from a single digit, 0 is not counted, f(1)=8, g(1)=1, just check whether there is 3 or not.

Now it is an N-digit XY. Think about it, if Y has an odd number of 3s and X has an odd number of 3s, then the f function is eutectic; if Y has an even number of 3s and X has an even number of 3s, then the f function is euclidean. If Y has an odd number of 3s and X has an even number of 3s, then the g function is Euclidean; if Y has an even number of 3s and X has an odd number of 3s, then the g function is Euclidean. Finally, we set X as the highest digit and Y as the last N-1 digits for recursion. In this case, X cannot be 0, which determines f(1)=8 instead of 9. In the end, 0 still needs to be considered, but it is used as the last n-1 bit, which is reflected in the multiplier 9 on the right side of the following derivation.

I have some ideas, and now write the derivation of f and g. Boundary: f(1)=8, g(1)=1. If the nth bit is 3, then add g(n-1); if the nth bit is not 3, then add 9*f(n-1), because there are 9 possibilities if it is not 3, multiplication principle.

Sort it out:

  • f(1)=8,g(1)=1
  • f(n)=g(n-1)+9*f(n-1)
  • g(n)=f(n-1)+9*g(n-1)

Write code:

#include <iostream>

using namespace std;

int g(int n);
int f(int n) {
	return n==1?8:(g(n-1)+9*f(n-1))%12345;
}

int g(int n) {
	return n==1?1:(f(n-1)+9*g(n-1))%12345;
}

int main() {
	int n;
	cin>>n;
	cout<<f(n);
	return 0;
}

It runs significantly faster.

Method 3: Dynamic Programming

The second method still needs to be improved. The f and g functions have repeated recursive calls, of course, it can be done by memoization . Since there is a recursive formula here, the state transition equation is ready to come out, and it has been written in the second method.

#include <iostream>

using namespace std;

int f[10002][2]; //f[][0]=even 3, f[][1]=odd 3

int main() {
	int n;
	cin>>n;
	f[1][0]=8,f[1][1]=1;
	for (int i=2;i<=n;i++) {
		f[i][0]=(9*f[i-1][0]+f[i-1][1])%12345;
		f[i][1]=(f[i-1][0]+9*f[i-1][1])%12345;
	}
	cout<<f[n][0];
	return 0;
}

Method 4: Play the table method

slightly.

Method 5: Formula method

I never thought it could be done with a formula! The Fibonacci sequence also has a general term formula, but how to ask for it? (of course refer to the book)

v2-f95a8addc9a192fb3a613e7daa03d402_hd

Write code:

#include <iostream>

using namespace std;

#define MOD 12345

// Fast exponentiation modulo 
int fast( int a, int N, int mod) {
     long  long r = 1, aa=a;
     while (N) {
    	 //To take the binary bits of N, multiply one by the corresponding exponentiation and combine remainder 
        if (N & 1) r = (r * aa) % mod;
        N >>= 1;
        aa = (aa * aa) % mod;
    }
    return (int)r;
}

// Fast exponentiation modulo (base 2) 
int fast2( int N, int mod) {
	 static  long  long a=(1LL<<62)%mod;
	 int s=N%62,t=N/62; // 2^N=2^s*a^t 
	int r = (1LL<<s) % mod;
	 if (t>0) {
		r *= fast(a,t,mod);// 2^s*a^t % mod
		r %= mod;
	}
    return (int)r;
}

int main() {
	int n;
	cin>>n;
	//化简:
	// an=1/2*{7*2^(3n-3)+9*2^(n-1)*5^(n-1)}
	// an=2^(n-2)*{9*5^(n-1)+7*2^(2n-2)} 
	int a=fast2(n-2,MOD);
	int b=a<<1;
	int ans=a*(9*fast(5,n-1,MOD)+7*((b*b)%MOD));
	ans% = MOD;
	cout<<ans<<endl;
	return 0;
}

It can be seen that for optimization, the code does not look very beautiful. If the title does not require precise values, then using floating point numbers and pow I think it should be able to make the speed a little faster.

In comparison, the dynamic programming method is actually the most concise and efficient .

Summarize

A topic, multiple methods, in fact, in essence, doing it with the thinking of a computer is naturally DP, and doing it with the thinking of a mathematician is to deduce the formula of the general term. However, there are powers in the general term formula, making it inherently inefficient for a computer to do it.

From the perspective of multi-threading optimization, the essence of the DP method is a layer-by-layer recursive calculation. The latter depends on the former. The calculation is not independent and cannot be decomposed into small tasks. The fastest is O(n). The essence of the formula method is exponentiation, and exponentiation also has dependencies, and the sub-problems are the same, so there is no need to divide. The exhaustive method can guarantee the independence of the subtasks, but the amount of calculation is still large, if and only if there is no other good method.

Formula derivation is complex and time-consuming, so dynamic programming is excellent.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325156229&siteId=291194637