1. 程式人生 > 其它 >CSP202104C 校門外的樹

CSP202104C 校門外的樹

題目連結一道DP與基本數學結合的題目。


可以想到有一種dp方式即f[i]表示前i個障礙物的方案數。轉移方程: [i] = f[j] * calc (j,i),calc即在障礙j與障礙i之間分段擺東西。之後設d = a[i] - a[j] 那麼就在d中間取約數。演算法瓶頸在於calc,這樣比較暴力,若是我們列舉j時由i-1列舉到1,每當我們找到一個答案ans,把他加入到一個集合中,在該集合中的數在之後的列舉中便不再能當做答案,否則他一定撞到j這個障礙。倒著列舉每次約數由小至大,可以大幅度剪枝。 注意每個數的約數可以預先篩好。

點選檢視程式碼
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
#include<bitset>
#include<queue>
#include<vector>
#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
const int mod = 1e9+7;
int ad(int x,int y) {
	x+=y; return x>=mod?x-mod:x;
}
int mu(int x,int y) {
	return 1ll*x*y%mod;
}
set<int>se;
int n;
int a[1005],f[1005];
vector<int>ve[100005];

int calc(int x,int y) {
	int d = a[y] - a[x];
	se.insert(d);
	int s = ve[d].size(),ans=0;
	for(int i=0;i<s;i++) {
		int o = ve[d][i];
		if(se.find(o)!=se.end()) continue;
		se.insert(ve[d][i]);
		ans++;
	}
	return ans;
} // calc j-->i

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=a[n]/2;i++) {
		for(int j=i*2;j<=a[n];j+=i) {
			ve[j].push_back(i);
		}
	}
	f[1] = 1;
	for(int i=2;i<=n;i++) {
		se.clear();
		for(int j=i-1;j>=1;j--) {
			f[i] = ad(f[i],mu(f[j],calc(j,i)));
		}
	}
	printf("%d",f[n]);
   return 0;
}