Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 535 Accepted Submission(s): 188
2 2 2 4 3 1 2 3
8 10
题目大意:
给定一个序列a,从中取出若干个数字求gcd,求解所有抽取方案的gcd之和
解题思路:
dp[i][j]表示序列a从0到i的数中,取出的gcd为j的方案数。那么对于每个i,dp[i+1][j]由三部分组成,选a[i+1]也选择前面的,只选a[i+1],只选前面的,所以有:
dp[i+1][j]=sigema(dp[i][m])(满足gcd(m,a[i+1])=j,m>=1&&m<=1000)
+dp[i][j]
+(a[i]==j)
最后的ans=sigema(dp[n-1][j]*j)(满足j>=1&&j<=1000)
p.s.:1.求解gcd(m,a[i+1])=j的数的时候,如果直接遍历会T,要先预处理,找到每一个和a[i+1]/j互素的数字,把他乘上j就是m
2.dp数组别忘了可能超过int,要用ll+取摸
3.题干中说的不同方案其实是一个方案中有一个数下标和另一个方案里中不同就可以
<pre name="code" class="cpp">#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> #include<math.h> #include<queue> #include<stack> #define ll long long #define INF 0x3f3f3f3f #define C(a) memset(a,0,sizeof a) #define C_1(a) memset(a,-1,sizeof a) #define C_i(a) memset(a,0x3f,sizeof a) #define F(i,n) for(int i=0;i<n;i++) #define F(n) for(int i=0;i<n;i++) #define F_1(n) for(int i=n;i>0;i--) #define S(a) scanf("%d",&a); #define S2(a,b) scanf("%d%d",&a,&b); #define SL(a) scanf("%I64d",&a); #define SD(a) scanf("%lf",&a); #define P(a) printf("%d\n",a); #define PL(a) cout<<a<<endl; #define PD(a)printf("%lf\n",a); #define rush() int t;scanf("%d",&t);while(t--) #define mod 100000007 using namespace std; vector<int>v[1005]; ll dp[1005][1005]; int a[1005];; int gcd(int a, int b) { while (a&&b) { if (a > b)a = a%b; else b = b%a; } return a + b; } void init() { for (int i = 1; i < 1005; i++) for (int j = 1; j <1005; j++) if (gcd(i, j) == 1)v[i].push_back(j); } int main() { ll ans = 0; init(); rush() { ans = 0; C(a); C(dp); int n, tem; S(n); for (int i = 0; i < n; i++)S(a[i]); dp[0][a[0]] = 1; for (int i = 1; i < n; i++) for (int j = 1; j <= 1000; j++) { ll cnt = dp[i - 1][j] + (a[i] == j); int k = 2; if (!(a[i] % j)) { int tem=a[i]/j; for (int k = 0; k < v[tem].size()&&j*v[tem][k]<1005; k++) cnt = (dp[i - 1][j*v[tem][k]]+cnt)%mod; }//不预处理会超时 dp[i][j] = cnt; } for (int i = 1; i <= 1000; i++)ans = (ans + i*dp[n - 1][i]) % mod; PL(ans); } }