zoukankan      html  css  js  c++  java
  • 【BZOJ】3434: [Wc2014]时空穿梭

    http://www.lydsy.com/JudgeOnline/problem.php?id=3434

    题意:n维坐标中要找c个点使得c个点在一条线上且每一维的坐标单调递增且不能超过每一维限定的值m[i](n<=11, 2<=c<=20, m[i]<=100000)

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005, MD=10007;
    int C[N][19], g[21][N], f[21][12][N], a[12], p[N], pcnt, mu[N], MN;
    bool np[N];
    inline void check(int &x) { if(x>=MD || x<=-MD) x%=MD; }
    void init() {
    	mu[1]=1;
    	for(int i=2; i<=MN; ++i) {
    		if(!np[i]) p[++pcnt]=i, mu[i]=-1;
    		for(int j=1; j<=pcnt; ++j) {
    			int t=i*p[j]; if(t>MN) break;
    			np[t]=1;
    			if(i%p[j]==0) { mu[t]=0; break; }
    			mu[t]=-mu[i];
    		}
    	}
    	C[0][0]=1;
    	for(int i=1; i<=MN; ++i) {
    		C[i][0]=1;
    		for(int j=1; j<19; ++j) C[i][j]=C[i-1][j-1]+C[i-1][j], check(C[i][j]);
    	}
    	for(int c=2; c<=20; ++c)
    		for(int i=1; i<=MN; ++i)
    			for(int j=i, k=1; j<=MN; j+=i, ++k)
    				g[c][j]+=C[i-1][c-2]*mu[k], check(g[c][j]);
    	for(int i=1; i<=MN; ++i)
    		for(int c=2; c<=20; ++c) {
    			int t=1;
    			for(int j=0; j<=11; ++j) {
    				f[c][j][i]=t*g[c][i]+f[c][j][i-1];
    				t*=i; check(t); check(f[c][j][i]);
    			}
    		}
    }
    int nn[1005], cc[1005], mm[1005][12], m[12], n, c;
    typedef long long ll;
    inline void cal(const int &xx) {
    	memset(a, 0, sizeof a);
    	a[0]=1;
    	static int b, bb, i, t, k;
    	for(k=0; k<n; ++k) {
    		t=m[k]/xx;
    		b=((ll)t*m[k])%MD; bb=-(((ll)t*(t+1))>>1)%MD;
    		for(i=n; i>=1; --i)
    			a[i]=(a[i]*b+a[i-1]*bb)%MD, check(a[i]);
    		a[0]*=b; check(a[0]);
    	}
    }
    int main() {
    	int T;
    	scanf("%d", &T);
    	for(int cs=1; cs<=T; ++cs) {
    		scanf("%d%d", &nn[cs], &cc[cs]);
    		for(int i=0; i<nn[cs]; ++i)
    			scanf("%d", &mm[cs][i]), MN=max(MN, mm[cs][i]);
    	}
    	init();
    	for(int cs=1; cs<=T; ++cs) {
    		n=nn[cs]; c=cc[cs];
    		int mx=~0u>>1, ans=0;
    		for(int i=0; i<n; ++i) m[i]=mm[cs][i], mx=min(mx, m[i]);
    		for(int i=1, pos=0; i<=mx; i=pos+1) {
    			pos=mx+1;
    			for(int d=0; d<n; ++d) pos=min(pos, m[d]/(m[d]/i));
    			cal(i);
    			for(int d=0; d<=n; ++d) ans+=a[d]*(f[c][d][pos]-f[c][d][i-1]), check(ans);
    		}
    		printf("%d
    ", ((ans%MD)+MD)%MD);
    	}
    	return 0;
    }
    

      

    题解:

    我真的不想写公式了= =QAQ

    本题最神的两个地方,也就是我没有想到的地方:

    1、问题的转化:我们只需要在这些多维空间中确定两个端点,那么所有的点一定在这两个端点的线段上边就是了,而且不会重合。那么我们可以枚举两个端点的坐标差$i和j$(为了方便我们先设二维的,然后再推广),由于线端上整点的的数目就是$gcd(i, j)-1$(不包含端点),所以我们可以立即得到公式(待会写)。

    2、公式的处理。由于推出公式后,会发现有一部分不能分块QAQ,因为是枚举$gcd$,而式子里边存在着乘法.....因此稍加处理(下边写)..

    那么本题如果没有以上两点就是纯水题了.....

    一下均设$m[i]<=m[j], i<=j$,因为这是没有影响的,下标是二维的情形

    容易得到公式:

    $$
    egin{align}
    ans
    = & sum_{i=1}^{m[1]} sum_{j=1}^{m[2]} (m[1]-i)(m[2]-j) inom{(i, j)-1}{c-2} \
    = & sum_{d=1}^{m[1]} sum_{i=1}^{ left lfloor m[1]/d ight floor } sum_{j=1}^{ left lfloor m[2]/d ight floor } [(i, j)=1] (m[1]-id)(m[2]-jd) inom{d-1}{c-2} \
    end{align}
    $$

    $$ f(d) = sum_{i=1}^{ left lfloor m[1]/d ight floor } sum_{j=1}^{ left lfloor m[2]/d ight floor }
    [(i, j)=1] (m[1]-id)(m[2]-jd) $$
    $$ F(d) = sum_{i=1}^{ left lfloor m[1]/d ight floor } sum_{j=1}^{ left lfloor m[2]/d ight floor }
    (m[1]-id)(m[2]-jd) $$

    $F(d)$计算的是$(i, j)=kd$时情况,$f(d)$计算的是$(i, j)=d$时的情况,所以

    $$ F(d) = sum_{d|n} f(n) $$

    那么就能反演啦...即

    $$ f(d) = sum_{d|n} mu ( frac{n}{d} ) F(n) $$

    最后推得:

    $$
    ans = sum_{i=1}^{m[1]} left( left lfloor frac{m[1]}{i} ight floor m[1] - i frac{ left lfloor frac{m[1]}{i} ight floor ( left lfloor frac{m[1]}{i} ight floor + 1 ) }{2} ight) left( left lfloor frac{m[2]}{i} ight floor m[2] - i frac{ left lfloor frac{m[2]}{i} ight floor ( left lfloor frac{m[2]}{i} ight floor + 1 ) }{2} ight) sum_{d|i} mu ( frac{i}{d} ) inom{i-1}{c-2}
    $$

    发现后边和$m$是无关的= =,因此拓展就是将前边的东西合起来= =,即

    $$
    ans = sum_{i=1}^{m[1]} prod_{j=1}^{n} left( left lfloor frac{m[j]}{i} ight floor m[j] - i frac{ left lfloor frac{m[j]}{i} ight floor ( left lfloor frac{m[j]}{i} ight floor + 1 ) }{2} ight) sum_{d|i} mu ( frac{i}{d} ) inom{i-1}{c-2}
    $$

    $$ g(i) = sum_{d|i} mu ( frac{i}{d} ) inom{i-1}{c-2} $$

    然后$O(nlnn)$的暴力求出$g(i)$你总会的吧= =

    但是现在关键是答案里面有个$i$的乘法因子 QAQ否则就能直接出解了..

    可是发现这样乘起来是关于$i$的多项式...那么设系数向量$a$,且仅当$ left lfloor frac{m[j]}{i} ight floor 不变时 $,有

    $$
    sum_{j=0}^{n} a_ji^j =
    prod_{j=1}^{n} left( left lfloor frac{m[j]}{i} ight floor m[j] - i frac{ left lfloor frac{m[j]}{i} ight floor ( left lfloor frac{m[j]}{i} ight floor + 1 ) }{2} ight)
    $$

    最后原式变为

    $$
    egin{align}
    ans_x
    = & sum_{i=1}^{m[1]} sum_{j=0}^{n} a_ji^j g(i) \
    = & sum_{j=0}^{n} a_j sum_{i=1}^{m[1]} i^j g(i) \
    end{align}
    $$

    然后暴力预处理你怕不怕= =

    时间复杂度为:$O(cmlog(n)+Tn^3sqrt{m})$

  • 相关阅读:
    Swagger 专题
    Spring boot中使用springfox来生成Swagger Specification小结
    Android导航菜单横向左右滑动并和下方的控件实现联动
    Android 日历控件 mCalendarView
    22个值得收藏的android开源代码-UI篇
    java获得指定日期的前一天,后一天的代码
    Java获取当前日期的前一个月,前一天的时间
    Android 获取当前日期算前一年、前一月、前一天Calendar
    [Android]通过setImageURI设置网络上面的图片
    Android-PullToRefresh 使用心得
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4271303.html
Copyright © 2011-2022 走看看