CSP202104C 校門外的樹
阿新 • • 發佈:2021-09-05
題目連結一道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; }