传送门
- 求在 \(n\) 的约数中选一些数使得任意两数之间不存在约数关系,问最多能选多少个数
如果显式地建出来边的话可以发现是个 DAG
于是就是求 DAG 上最长反链长度,可以 Dilworth 定理转为最小链覆盖然后跑网络流了
然而有更优的解法:

一个结论:令 \(f(i)\) 为 \(i\) 的所有质因子的指数之和,令 \(cnt_i\) 为满足 \(f(x)=i\) 的 \(x\) 的数量
则最长反链长度为 \(cnt_{\frac{f(n)}{2}}\)
上面证明过程中强制 \(\operatorname{deg}(d_1)+\operatorname{deg}(d_2)=\operatorname{deg}(n)\) 是为了确保这条链经过一个 \(f(i)=\frac{f(n)}{2}\) 的点
那现在证明了满足上述条件的最小链覆盖为 \(cnt_{\frac{f(n)}{2}}\)
如何证明不必满足上述条件时的最小链覆盖也是 \(cnt_{\frac{f(n)}{2}}\) 呢?
发现每个 \(f(i)=\frac{f(n)}{2}\) 的 \(i\) 所在的链两两不同,所以 \(cnt_{\frac{f(n)}{2}}\) 已经达到可能的链覆盖下界了
那现在背包 DP 求出 \(cnt_{\frac{f(n)}{2}}\) 就可以了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int divd[N], dcnt;
namespace force{
int sta[N], ans=1, top;
vector<int> rec;
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
inline int lcm(int a, int b) {return a*b/gcd(a, b);}
void solve() {
int lim=1<<dcnt;
for (int s=1,cnt; s<lim; ++s) {
top=0;
for (int i=1; i<=dcnt; ++i) if (s&(1<<(i-1))) sta[++top]=divd[i];
for (int i=1; i<=top; ++i)
for (int j=i+1; j<=top; ++j) {
// assert(n%lcm(sta[i], sta[j])==0);
if (sta[i]*lcm(sta[i], sta[j])+sta[j]*gcd(sta[i], sta[j])<=2*sta[i]*sta[j]) goto jump;
}
cnt=__builtin_popcount(s);
if (cnt>ans) ans=cnt, rec.clear(), rec.pb(s);
else if (cnt==ans) rec.pb(s);
jump: ;
}
cout<<ans<<endl;
// for (auto it:rec) {for (int i=1; i<=dcnt; ++i) if (it&(1<<(i-1))) cout<<sta[i]<<' '; cout<<endl;}
}
}
// namespace task1{
// int ans;
// bool vis[N];
// void solve() {
// if (dcnt<=2) {puts("1"); exit(0);}
// for (int i=2; i<dcnt&&!vis[i]; ++i)
// for (int j=i+1; j<dcnt; ++j)
// if (divd[j]%divd[i]==0) vis[j]=1;
// for (int i=2; i<dcnt; ++i) if (!vis[i]) ++ans, cout<<i<<' '; cout<<endl;
// cout<<ans<<endl;
// }
// }
namespace task3{
int ans;
bool vis[N];
int solve() {
for (int i=1; i<=dcnt&&!vis[i]; ++i)
for (int j=i+1; j<=dcnt; ++j)
if (divd[j]%divd[i]==0) vis[j]=1;
for (int i=1; i<=dcnt; ++i) if (!vis[i]) ++ans;
return ans;
}
}
namespace task2{
bool vis[N];
int sta[N], tem[N], ans, top, top2;
void solve() {
ans=max(ans, task3::solve());
for (int i=1; i<=dcnt; ++i) if (!task3::vis[i]) sta[++top]=divd[i];
int cnt=0;
while (cnt%10 || clock()<930000) {
int t=rand()%dcnt+1;
for (int i=1; i<=top; ++i) if (sta[i]%divd[t]==0 || divd[t]%sta[i]==0) vis[i]=1;
top2=0;
for (int i=1; i<=top; ++i)
if (!vis[i]) tem[++top2]=sta[i];
else vis[i]=0;
for (int i=1; i<=top2; ++i) sta[i]=tem[i];
top=top2;
sta[++top]=divd[t];
ans=max(ans, top);
++cnt;
}
cout<<ans<<endl;
}
}
namespace task{
pair<int, int> div[N];
int f[1010][1010], dcnt;
void solve() {
int m=n, sum=0;
for (int i=2; i*i<n; ++i) if (m%i==0) {
div[++dcnt]={i, 0};
do {m/=i; ++div[dcnt].sec; ++sum;} while (m%i==0);
}
if (m>1) div[++dcnt]={m, 1}, ++sum;
f[0][0]=1;
for (int i=1; i<=dcnt; ++i)
for (int j=0; j<=div[i].sec; ++j)
for (int k=j; k<=sum/2; ++k)
f[i][k]+=f[i-1][k-j];
cout<<f[dcnt][sum/2]<<endl;
}
}
signed main()
{
freopen("divisor.in", "r", stdin);
freopen("divisor.out", "w", stdout);
n=read();
// for (int i=1; i*i<=n; ++i) if (n%i==0) {
// divd[++dcnt]=i;
// if (i*i!=n) divd[++dcnt]=n/i;
// }
// sort(divd+1, divd+dcnt+1);
// // cout<<"div: "; for (int i=1; i<=dcnt; ++i) cout<<divd[i]<<' '; cout<<endl;
// if (dcnt<=2) {puts("1"); exit(0);}
// --dcnt; swap(divd[1], divd[dcnt]); --dcnt;
// sort(divd+1, divd+dcnt+1);
// // if (dcnt<=22) force::solve();
// // else task2::solve();
task::solve();
return 0;
}