题解 P2714 【四元组统计 】
command_block · · 题解
题意 : 给出
多组数据,
-
类似的问题 : 给出若干个
[0,2^n) 内的数,对m=[0,2^n) 求选取k 元组and
起来为m 的方案数.开个桶记录出现次数,设为
F[n] .先跑高维前缀和,然后
F[n] 就变成了“是n 的超集的个数”我们令
F[n]=\dbinom{F[n]}{k} ,即“且起来是是n 的超集的k 元组个数”然后跑高维差分,即可得到“且起来是是
n 的k 元组个数”。这道题告诉我们,对于按位取
\min/\max 的运算,其转移是个DAG
,这类变换除了线性性之外往往还有其他优秀性质。
回到本题,其实gcd
就是质因数向量上的
先做一个约数前缀和,然后就变为了点值,每个点值变成
这样甚至能得到
容易发现,改成
复杂度可以用P5495 Dirichlet 前缀和做到
代码很好写 :
#include<algorithm>
#include<cstdio>
#define MaxN 20050000
#define ll long long
using namespace std;
inline int read(){
register int X=0;
register char ch=0;
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar();
return X;
}
ll F[MaxN];
int p[MaxN/12],tn;
void solve()
{
int m,n=1;
if (scanf("%d",&m)==EOF)exit(0);
for(int i=1,t;i<=m;i++){
F[t=read()]++;
n=max(n,t);
}
for(int i=1;p[i]<=n;i++)
for(int j=n/p[i];j;j--)
F[j]+=F[j*p[i]];
for(int i=1;i<=n;i++)F[i]=(F[i]-3)*(F[i]-2)*(F[i]-1)*F[i]/24;
for(int i=1;p[i]<=n;i++)
for(int j=1;j*p[i]<=n;j++)
F[j]-=F[j*p[i]];
printf("%lld\n",F[1]);
for(int i=1;i<=n;i++)F[i]=0;
}
bool e[MaxN];
int main()
{
for(int i=2;i*i<=10000;i++)
if (!e[i])
for (int j=i*i;j<=10000;j+=i)
e[j]=1;
for(int i=2;i<=10000;i++)
if (!e[i])p[++tn]=i;
p[++tn]=10001;
while(1)solve();
return 0;
}
似乎利用