原題
題解
一道非常傻逼,非常傻逼的暴力題,一點都不優雅,這能放普及 T4 真是開了眼了。
本題難點主要就是在時間複雜度的計算上,只要算對了並且有勇氣去打就能 AC 。
首先發現能形成一個點集,當且僅當所有點從小到大排序後,後面的點是前面所有點的倍數。
因此,我們只要保證點集中的數 \(b_i = k * b_{i-1}\) 即可。
於是,狀態轉移方程就這樣推出來了。
\(b_i = k * b_{i-1} (2\le k\le \frac{10^6}{b_{i-1}})\) , 列舉 \(k\) 與 \(b_{i-1}\) 即可。
注意要提前離散一下所有數,然後開個 map 或 陣列 判斷某個數是否存在。
幾個重要的性質:
-
一個點集最多有 $log n $ 種數。
-
由於對於所有 \(b\) 列舉 \(k\) 的時間為 $ \frac{10^6}{1} + \frac{10^6}{2} + \frac{10^6}{3} + \text{...}$ $ + \frac{10^6}{1000000-1} + \frac{10^6}{1000000} $ ,即它是一個調和級數,時間為 $O(ln n) \approx O(log n) $,可以過。
同樣可以理解為一個反比例函式,然後分段求下和也可以證明這個複雜度正確。
注意,狀態轉移要從小往大轉移。
賽時早就想出做法但不敢寫,在比賽最後 20min 的時候才A掉這題的我是個傻逼。
#include <bits/stdc++.h>
using namespace std;
int n,a;
vector<int> v;
struct num{
int val,t=0,dp=0;//t為其出現次數,dp為動態規劃
};
vector<num> vct;
int m[1000005];
int main()
{
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a;
v.push_back(a);
}
//離散化
sort(v.begin(),v.end());
memset(m,-1,sizeof(m));
int tmp=0;
for(auto i:v)
{
if(tmp==0 || v[tmp-1]!=i)
{
vct.push_back({i,1});
m[i]=vct.size()-1;
}
else vct[vct.size()-1].t++;
tmp++;
}
//動態規劃
int maxans=-1;
for(int i=0;i<vct.size();i++)
{
vct[i].dp=max(vct[i].dp,vct[i].t);
int x=vct[i].val,y=vct[i].t,z=vct[i].dp;
for(int j=2;j*x<=1000000;j++)
{
if(m[j*x]!=-1)
{
vct[m[j*x]].dp=max(vct[m[j*x]].dp,vct[m[j*x]].t+z);
}
}
maxans=max(maxans,vct[i].dp);
}
cout<<maxans;
return 0;
}