BZOJ3434: [Wc2014]时空穿梭

来源:互联网 发布:电视机屏幕检测软件 编辑:程序博客网 时间:2024/04/28 10:53

我们考虑怎么选取c个点满足有直线同时经过他们
Ai表示第i个点,用向量bi=(x1,x2,x3...xn)表示Ai+1Ai的差
由于每一维的vixi=pi+tvi是不变的,对于相邻的两个点,Δt是相同的,所以任一向量bi=(x1,x2,x3...xn)xi之间的比值是不变的,即vi之间的比值,所以对于任一向量bi=(x1,x2,x3...xn),都可以将它写成bi=(Δtv1,Δtv2...Δtvn),将vi之间的比值写成整数ai之间的比值,得bi=(kc1,kc2,...kcn)

我们考虑枚举每一维的极差a1,a2,...an,设d=gcd(a1,a2,...an),则ai=cid,则将d分给这c1bi,共有Cc2d1种分法,原理是插板法,即因为每一维严格递增,所以每个bi分到的不能为0,相当于1d,之间有d1个空位,在这些中选出c2个位置插隔板代表这c1个数之间的分界线(感觉没讲清楚,意会一下?)
因为所有坐标为正整数,第i维坐标不超过Mi,所以极差不超过Mi1
mi=Mi1
所以有

ans=a1=1m1(M1a1)a2=1m2(M2a2)...an=1mn(Mnan)Cc2gcd(a1,a2...an)1

=dCc2d1ni=1midai=1(Miaid) ϵ((a1,a2,...an)=1)
反演一下

=dCc2d1ni=1midai=1(Miaid)D|(a1,a2...an)μ(D)

=dCc2d1Dμ(D)ni=1miDdai=1(MiaiDd)

枚举D=Dd
=Dd|DCc2d1μ(Dd)ni=1miDai=1(MiaiD)

可以发现f(D,c)=d|DCc2d1μ(Dd)这个东西只与Dc有关,c的范围不大,可以对所有的Dc预处理出f(D,c)(枚举c后,对每个d,枚举它的倍数D,根据调和级数,这样总的复杂度是O(cmlogm)

到这里,因为后面的柿子和D仍然相关,仍然需要枚举mD,因为m很大,而且有多组数据,观察到使每个miD相同的D最多有O(nm)段,这个复杂度可以接受,所以考虑继续化,

观察ni=1miDai=1(MiaiD)这个东西,可以化成
ni=1MimiD(D+DmiD)miD2

=ni=1MimiDD(1+miD)miD2

接着把这n个多项式相乘O(n2)展开成形如a0+a1D+a2D2+.....anDn的多项式,对于Di的系数ai,只与miD有关

所以在维护了g(D,c,j)=f(D,c)Dj的前缀和后,可以将原式按miD分成O(nm)段,每段内暴力O(n2)展开多项式各项系数,乘上前面的柿子这一段的和

时间复杂度O(nmc+cmlogm+Tn3m)

code:

#include<set>#include<map>#include<deque>#include<queue>#include<stack>#include<cmath>#include<ctime>#include<bitset>#include<string>#include<vector>#include<cstdio>#include<cstdlib>#include<cstring>#include<climits>#include<complex>#include<iostream>#include<algorithm>#define ll long longusing namespace std;inline void down(int &x,const int y){if(x>y)x=y;}const int maxn = 12;const int maxc = 21;const int maxm = 110000;const int Mod = 10007;int p[maxm],pri,miu[maxm];bool v[maxm];int C[maxm][maxc],f[maxc][maxm],pg[maxc][maxm][maxn];//                 miu*C            pre G(i,c)×i^jvoid pre(){    C[0][0]=1;    for(int i=1;i<maxm;i++)    {        C[i][0]=1;        for(int j=1;j<maxc;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;    }    miu[1]=1; ///////////////////////////////////////////////////////////    for(int i=2;i<maxm;i++)    {        if(!v[i]) miu[i]=-1,p[++pri]=i;        for(int j=1;j<=pri;j++)        {            int k=i*p[j];            if(k>=maxm) break;            v[k]=true;            if(i%p[j]==0) break;            miu[k]=-miu[i];        }    }    for(int c=2;c<maxc;c++)    {        for(int i=1;i<maxm;i++)        {            for(int j=i,ji=1;j<maxm;j+=i,ji++)                (f[c][j]+=C[i-1][c-2]*miu[ji]%Mod)%=Mod;        }    }    for(int c=2;c<maxc;c++)    {        for(int i=1;i<maxm;i++)        {            for(int j=0,ij=1;j<maxn;j++,ij=ij*i%Mod)            {                pg[c][i][j]=(pg[c][i-1][j]+f[c][i]*ij%Mod)%Mod;            }        }    }}int n,c;int m[maxn];int a[maxn],an;int main(){    pre();    int t; scanf("%d",&t);    while(t--)    {        int mn=maxm;        scanf("%d%d",&n,&c);        for(int i=1;i<=n;i++) scanf("%d",&m[i]),m[i]--,down(mn,m[i]);        int ret=0;        for(int i=1;i<=mn;)        {            int r=mn;            for(int j=1;j<=n;j++) down(r,m[j]/(m[j]/i));            a[an=0]=1;            for(int j=1;j<=n;j++)            {                int mi=(m[j]/i)%Mod;                int x1=(m[j]+1)*mi%Mod,x2=-mi*(mi+1)/2%Mod;                a[an+1]=a[an]*x2%Mod;                for(int l=an;l>=0;l--) a[l]=(a[l]*x1%Mod+(l?a[l-1]:0)*x2%Mod)%Mod;                an++;            }            for(int j=0;j<=an;j++) (ret+=a[j]*((pg[c][r][j]-pg[c][i-1][j])%Mod)%Mod)%=Mod;            i=r+1;        }        if(ret<0) ret+=Mod;        printf("%d\n",ret);    }    return 0;}
0 0
原创粉丝点击