LYK loves integer split (good question for dp)

Topic

LYK is currently working on the problem of integer splitting. He believes that a number can be split into the sum of several positive integers. For example, when
n=5. There are several ways to split.
5=1+1+1+1+1
5=1+1+1+2
5=1+1+3
5=1+2+2
5=1+4
5=2+3
5=5
For one In this way of splitting, the value generated is the sum of the gcd of each logarithm.
For example, 5=1+2+2, the value it produces is gcd(1,2)+gcd(1,2)+gcd(2,2)=4. For the 5=5 split method,
the value generated is 0.
Of course, LYK also has some numbers that it doesn't like. It wants to know what is
the result of modulo 1000000007 for the sum of all the splitting methods without the numbers it doesn't like .

Input format

Several sets of data, no more than 5 sets.
For each group of data, there is a number n in the first row.
The first number m in the next line indicates that there are m numbers that LYK doesn't
like , and the next number m indicates the numbers that LYK doesn't like.

Output format

Output several lines to indicate the answer.

data range

n<=2000

1500 ms, 128 mb

answer

n <= 2000, which is a relatively gratifying data range.

So the core of this question is to design a good dp and find a good enumeration method, which can be in O (n 2 log ⁡ n) O(n^2\log n)O ( n2logn ) Over the range.

I said directly, we can finally enumerate the contribution of the number to the calculation of gcd, so we first find the number of splitting schemes of n, let f [i] f[i]f [ i ] indicates whether LYK does not like i.

d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] is to split i, and the largest number is less than or equal to the number of schemes j (the second dimension is purely to facilitate transfer), then the following equation:
dp [i] [j] = ∑ k = 1, f [k] = 0 jdp [i − k] [k] dp[i][j]=\sum_{k=1,f[k]=0}^{j}dp[ik] [k]dp[i][j]=k=1,f[k]=0jdp[ik][k]

It’s not easy to do it right, let’s go the other way, then dp[i][j](f[j]=0) has a contribution to dp[i+j][j~n], which is equivalent to the following paragraphs. To add, we make a mark, with prefix and optimization can do O (n 2) O(n^2)O ( n2)

Then we enumerate the number pairs (i, j) (i, j)(i,j ) Seek their contribution. At this time, the enumeration method, or the definition method, is more coincidental.

First i = ji=ji=Needless to say for the case of j , the x-th i from left to right in the enumerating split scheme, and then the contribution to the answer is(dp [n − xi] [n] ∗ i) ∗ (x − 1) ( dp[n-xi][n]*i)*(x-1)(dp[nxi][n]i)(x1 ) , that is, it forms a pair with each one on its left.

Consider the splitting scheme (i, j) (i, j)(i,j ) There may be more than one, we will enumerate(x, y) (x, y)on this basis(x,y ) , which meansthe number pair formed by the x-th i and the y-th j from left to right in thesplitting scheme, then a kind of "(i, j), (x, y) (i,j),(x ,y)(i,j),(x,y ) ”The contribution to the answer isdp [n − xi − yj] [n] ∗ gcd (i, j) dp[n-xi-yj][n]*gcd(i,j)dp[nx iy j ] [ n ]gcd(i,j ) , this will not be considered heavy.

Then do we enumerate like this?

for(int i = 1;i <= n;i ++) {
    
    
	for(int j = i + 1;j <= n;j ++) {
    
    
		for(int x = 1;x * i <= n;x ++) {
    
    
			for(int y = 1;y * j + x * i <= n;y ++) {
    
    
				ans += ...
			}
		}
	}
}

According to the analysis of the complexity of the sieve, we can judge that the complexity is O (n 2 ln ⁡ 2 n) O(n^2\ln^2n)O ( n2ln2n ) , one moreln ⁡ \lnln , can’t pass, then we can actually comparedpaccording to the difference of j[...] [n] dp[...][n]d the p- [ . . . ] [ the n- ] obtained and the j prefix, so that only enumeration x, do not enumerate y, and we used the prefix optimize and save a log.

CODE

#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 2005
#define LL long long
#define ULL unsigned long long
#define DB double
#define ENDL putchar('\n')
#define eps 1e-5
#pragma GCC optimize(2)
LL read() {
    
    
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {
    
    if(s == '-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {
    
    x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
bool f[MAXN];
int dp[MAXN][MAXN],tag[MAXN][MAXN];
int sm[MAXN][MAXN];
int gcd(int a,int b) {
    
    return b == 0 ? a:gcd(b,a % b);}
int main() {
    
    
	freopen("zscf.in","r",stdin);
	freopen("zscf.out","w",stdout);
	while(scanf("%d",&n) == 1) {
    
    
		m = read();
		memset(f,0,sizeof(f));
		memset(dp,0,sizeof(dp));
		memset(sm,0,sizeof(sm));
		memset(tag,0,sizeof(tag));
		for(int i = 1;i <= m;i ++) s = read(),f[s] = 1;
		for(int i = 1;i <= n;i ++) {
    
    
			dp[0][i] = 1;
			if(!f[i]) (tag[i][i] += 1) %= MOD;
		}
		for(int i = 1;i <= n;i ++) {
    
    
			int ss = 0;
			for(int j = 1;j <= n;j ++) {
    
    
				(ss += tag[i][j]) %= MOD;
				dp[i][j] = ss;
				if(!f[j] && i+j <= n) (tag[i+j][j] += dp[i][j]) %= MOD;
			}
		}
		for(int i = 1;i <= n;i ++) {
    
    
			for(int j = 0;j <= n;j ++) {
    
    
				sm[i][j] = dp[j][n];
				if(j >= i) (sm[i][j] += sm[i][j-i]) %= MOD;
			}
		}
		int ans = 0;
		for(int i = 1;i <= n;i ++) {
    
    
			if(f[i]) continue;
			for(int j = i+i,k = 1;j <= n;j += i,k ++) {
    
    
				(ans += dp[n-j][n] *1ll* k % MOD *1ll* i % MOD) %= MOD;
			}
			for(int j = i+1;j <= n;j ++) {
    
    
				if(f[j]) continue;
				int gc = gcd(i,j);
				for(int k = i;k <= n && k+j <= n;k += i) {
    
    
					(ans += sm[j][n-k-j] *1ll* gc % MOD) %= MOD;
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_43960414/article/details/114443875