测试
在你的帮助下,(Winedag) 愉快地在$ deadline $前造完了他的题目。
虽然 (Winedag) 非常牛逼,但 (Wetmath) 还是要求他参加模拟考试。
(Winedag) 要考的题目一共有 (n) 道,对于第$ i $道题,如果 (AC) 了,那么得分为(i) ,否则得分为(1),这套题的得分为所有题的得分的积,如果没有$ AC$ 任何题,那么得分为 (1) 。
就在 (Winedag) 准备切题如麻的时候,突然想起了一个事情,之前有人告诉他:在评测结束以后,会有一个小斗士来把所有分数不为完全平方数的人的程序删掉,分数变为 (0) 分。
(Winedag) 想知道他有多少种方案能得到他能得到的最高分,但是他忙于写代码,所以他只能来找你帮忙。
显然我们得到最大乘积的方案就是先把所有的数乘起来,然后如果有些质因子出现次数为奇数就将该质因子除去。合法的方案即使选出一些不具有相同质因子数,他们质因子最高次数为(1),并且他们恰好包含了所有需要的质因子。
这种质因子的题大概率是将以(sqrt n)为分界线将质因子分为两个集合然后状压。小于(sqrt {3000})的质因子只有(16)个,于是状压就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 3005
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
bool vis[N];
int p[N],size[N],n;
int sp[N];
void pre(int n) {
for(int i=2;i<=n;i++) {
if(!vis[i]) p[++p[0]]=i;
for(int j=1;j<=p[0]&&1ll*i*p[j]<=n;j++) {
vis[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
}
vector<int>st[N];
ll f[1<<16],g[1<<16];
int main() {
n=Get();
pre(n);
for(int i=1;i<=n;i++) {
int v=i;
for(int j=1;j<=p[0];j++) {
while(v%p[j]==0) {
size[p[j]]^=1;
v/=p[j];
}
}
}
int tot=0;
for(int i=1,maxx=sqrt(n);i<=p[0]&&p[i]<=maxx;i++) {
if(size[p[i]]) sp[++sp[0]]=p[i];
}
for(int i=1;i<=n;i++) {
int flag=1;
int sta=0;
int v=i;
for(int j=1;j<=sp[0];j++) {
if(v%sp[j]==0) {
sta|=1<<j-1;
v/=sp[j];
if(v%sp[j]==0) {flag=0; break;}
}
}
if(!flag) continue ;
if(v>1&&(vis[v]||!size[v])) continue ;
st[v].push_back(sta);
}
f[0]=1;
for(int i=0;i<st[1].size();i++) {
memcpy(g,f,sizeof(g));
for(int S=0;S<1<<sp[0];S++)
if((st[1][i]&S)==st[1][i])
g[S]+=f[S^st[1][i]];
memcpy(f,g,sizeof(f));
}
for(int i=2;i<=n;i++) {
if(!st[i].size()) continue ;
memset(g,0,sizeof(g));
for(int j=0;j<st[i].size();j++) {
for(int S=0;S<1<<sp[0];S++)
if((st[i][j]&S)==st[i][j])
g[S]+=f[S^st[i][j]];
}
memcpy(f,g,sizeof(f));
}
cout<<f[(1<<sp[0])-1];
return 0;
}