hdu 5307 He is Flying 2015 Multi-University Training Contest 2 快速傅里叶变换

He is Flying

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 91    Accepted Submission(s): 25


Problem Description
JRY wants to drag racing along a long road. There are  n  sections on the road, the  i -th section has a non-negative integer length  si . JRY will choose some continuous sections to race (at an unbelievable speed), so there are totally  n(n+1)2  different ways for him to ride. If JRY rides across from the  i -th section to the  j -th section, he would gain  ji+1  pleasure. Now JRY wants to know, if he tries all the ways whose length is  s , what's the total pleasure he can get. Please be aware that in the problem, the length of one section could be zero, which means that the length is so trivial that we can regard it as  0 .
 

Input
The first line of the input is a single integer  T (T=5) , indicating the number of testcases. 

For each testcase, the first line contains one integer  n . The second line contains  n  non-negative integers, which mean the length of every section. If we denote the total length of all the sections as  s , we can guarantee that  0s50000  and  1n100000 .
 

Output
For each testcase, print  s+1  lines. The single number in the  i -th line indicates the total pleasure JRY can get if he races all the ways of length  i1 .
 

Sample Input
  
  
2 3 1 2 3 4 0 1 2 3
 

Sample Output
  
  
0 1 1 3 0 2 3 1 3 1 6 0 2 7
 

Source

题目:

      给一个数组有N个数字,每个数字都大于等于0,所有数字相加不大于50000, 对于区间[l,r]如果区间点数和为S‘,那么S'的值加上r-l+1。

令sum为所有数字的总和,对(N+1)*N/2个区间进行处理计算S'和增加对应的值,最后输出0到sum的每个数的值。

分析:

     普通的做法要N*N的,枚举所有的区间。但是数组太大会超时的。

     由于sum <= 50000考虑构造母函数进行求解 用Si表示前i个数字的前缀和。构造如下多项式,对于计算的结果A*X^Si表示区间和为Si能够得到值为A

会fft的应该就会懂这个多项式的意义(所以不多说)。

用X^Si * X*-Sj 得到的就是区间[j+1,i]的区间和,带上系数i,则表示区间长度为i。但是我们实际要得到的是i-j的值,所以第二个乘法的目的就是减去这个j。

最后0的情况特殊处理即可。FFT用double可能精度不够用long double好。标程用的是整数进行计算的。看不懂。我的模板用long double 就行了。在hdu上要用g++提交,不然也是会wa的。标程也是要用g++提交,不然就T啦。

   

还要注意:

        负数次幂要变成整数次幂,把sum算出来,每个负数次幂加上一个sum即可。

       在每个乘法中第二个括号内,要添加一个X^0的数,表示可以选着区间[1,x]否则可能算不出[1,x]的结果(x表示>=1,<=N)的数字


直接贴代码:

我的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define ld long double
struct Complex{
    ld real;
    ld imag;
    Complex (){}
    Complex(ld the){
        real = cos(the);
        imag = sin(the);
    }
    Complex(ld a,ld b){
        real = a;
        imag = b;
    }
};
Complex operator + (Complex a,Complex b){
    return Complex(a.real+b.real,a.imag+b.imag);
}
Complex operator - (Complex a,Complex b){
    return Complex(a.real-b.real,a.imag-b.imag);
}
Complex operator * (Complex a,Complex b){
    return Complex(a.real*b.real-a.imag*b.imag,a.imag*b.real+a.real*b.imag);
}

#define maxn 300000
ld pi2 = 2*acos(-1.0);
void fft(Complex* A,int len, int ref){
    //A[rev[k]] = ak 系数向量转置
    int bitn = log2(len);
    int i,j,k;
    for(i = 0;i < len; i++){
        k = 0;
        for( j = 0;j < bitn; j++){
            k = (k<<1);
            if(i&(1<<j))
                k |= 1;
        }
        if(k > i)
            swap(A[i],A[k]);
    }
    //fft计算得到点值
    Complex wm,w,t,u;
    for(int m = 2,f=1; m <= len; m<<=1,f<<=1){
        wm = Complex(ref*pi2/m);
        for( k = 0;k < len; k += m){
            w = Complex(1.0,0);
            for( j = k; j < k+f; j++){
                t = w*A[j+f];
                u = A[j];
                A[j] = u+t;
                A[j+f] = u-t;
                w = w*wm;
            }
        }
    }
    if(ref == -1){
        for( i = 0;i < len; i++){
            A[i].real = A[i].real/len;
        }
    }
}

Complex a1[maxn];
Complex a2[maxn];

int num[maxn];
int sum[maxn];
ll  ans[maxn];
ll snum[maxn];
int main(){
    int t,n;
    //freopen("h.in","r",stdin);
    //freopen("hh.out","w",stdout);
    scanf("%d",&t);
    snum[0] = 0;
    for(ll i = 1; i <= 100000; i++)
        snum[i] = snum[i-1] + i*(i+1)/2;
    while(t--){
        scanf("%d",&n);
        for(int i = 0;i < n; i++)
            scanf("%d",&num[i]);
        sum[0] = num[0];
        for(int i = 1;i < n; i++)
            sum[i] = sum[i-1] + num[i];
        ll p = 0 ,res = 0,lnum = 0;
        for(int i = 0;i < n; i++){
            if(num[i] == 0)
                p++;
            else {
                res += snum[p];
                p = 0;
            }
        }
        res += snum[p];
        printf("%I64d\n",res);

        int total = sum[n-1];
        int total2 = total*2;
        int len = 1;
        while(len <= total2) len*=2;
        memset(a1,0,sizeof(a1));
        memset(a2,0,sizeof(a2));
        for(int i = 0; i < n; i++){
            a1[sum[i]].real += i+1;
            if(i != n-1)
            a2[total-sum[i]].real += 1;
        }
        a2[total].real += 1;
        fft(a1,len,1);
        fft(a2,len,1);

        for(int i = 0;i <= len; i++)
            a1[i] = a1[i]*a2[i];
        fft(a1,len,-1);
        for(int i = 0;i <= len; i++)
            ans[i] = (ll)(a1[i].real+0.5);

        memset(a1,0,sizeof(a1));
        memset(a2,0,sizeof(a2));
        for(int i = 0;i < n; i++){
            a1[sum[i]].real += 1;
            if(i != n-1)
            a2[total-sum[i]].real += i+1;
        }
        fft(a1,len,1);
        fft(a2,len,1);

        for(int i = 0;i <= len; i++)
            a1[i] = a1[i]*a2[i];
        fft(a1,len,-1);
        for(int i = 0;i <= len; i++)
            ans[i] -= (ll)(a1[i].real+0.5);

        for(int i = total+1;i <= total2; i++)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}



标程的代码:确实厉害。留着以防不时之需

#include <cstdio>
#include <string.h>
using namespace std;

#define P 50000000001507329LL
#define G 3
int T;
int n, s[110000];
long long ans[140000], a[140000], b[140000], c[140000], x[140000], w[140000];
long long pu[110000];
int nn;

long long Mul(long long x, long long y) {
	return (x*y-(long long)(x/(long double)P*y+1e-3)*P+P)%P;
}

long long Pow(long long x, long long y) {
    long long i, ans = 1;
    for (i = 1; i <= y; i *= 2, x = Mul(x, x)) if (y & i)  ans = Mul(ans, x);
    return ans;
}

void DFT(long long *a, int n) {
    int m, i, d, p, q;
    for (m = 1; (1 << m) <= n; m++){
        for (i = 0; i < (n >> m); i++)
        for (q = 0, d = p = i; d < n; q += (n >> m), d += (n >> (m - 1)), p += (n >> m)){
            x[p] = (Mul(a[d + (n >> m)], w[q]) + a[d]) % P;
            x[p + n / 2] = (Mul(a[d + (n >> m)], w[q + n / 2]) + a[d]) % P;
        }
        for (i = 0; i < n; i++) a[i] = x[i];
    }
}

void DFT1(long long *a, int n){
    int m, i, d, p, q;
    for (m = 1; (1 << m) <= n; m++) {
        for (i = 0; i < (n >> m); i++)
        for (q = 0, d = p = i; d < n; q += (n >> m), d += (n >> (m - 1)), p += (n >> m)){
            x[p] = (Mul(a[d + (n >> m)], w[n - q]) + a[d]) % P;
            x[p + n / 2] = (Mul(a[d + (n >> m)], w[n / 2 - q]) + a[d]) % P;
        }
        for (i = 0; i < n; i++) a[i] = x[i];
    }
}

void doit() {
	long long S = Pow(n, P - 2);
	DFT(a, n);
	DFT(b, n);
	for (int i = 0; i < n; i++)
		c[i] = Mul(a[i], b[i]);
	DFT1(c, n);
	for (int i = 0; i < n; i++)
		c[i] = Mul(c[i], S);
	for (int i = 0; i < n; i++)
		ans[i] = (ans[i] + c[i]) % P;
	// for (int i = 0; i <= s[n]; i++)
	// 	for (int j = 0; j <= s[n]; j++)
	// 		ans[i + j] += 1LL * a[i] * b[j];
}

int main() {
	 //freopen("h.in", "r", stdin);
	 //freopen("h.out", "w", stdout);
	scanf("%d", &T);
	n = 131072;
	for (int i = 0; i <= n; i++)
		w[i] = Pow(G, (P - 1) / n * i);
    s[0] = 0;
	while (T--) {
		scanf("%d", &nn);
		for (int i = 1; i <= nn; i++)
			scanf("%d", &s[i]), s[i] += s[i - 1];
		memset(ans, 0, sizeof ans);
		memset(a, 0, sizeof a);
		memset(b, 0, sizeof b);

		for (int i = 1; i <= nn; i++)
			a[s[i]] += i;
		for (int i = 1; i <= nn; i++)
			b[s[nn] - s[i - 1]]++;
		doit();
		memset(a, 0, sizeof a);
		memset(b, 0, sizeof b);
		for (int i = 0; i <= 50000; i++)
			a[i] = b[i] = 0;
		for (int i = 1; i <= nn; i++)
			a[s[i]] = (a[s[i]] + P - 1) % P;
		for (int i = 1; i <= nn; i++)
			b[s[nn] - s[i - 1]] += i - 1;
		doit();
		long long ans0 = 0;
		int q, h;
		for (int i = 1; i <= nn; i++)
			pu[i] = pu[i - 1] + 1LL * i * (i + 1) / 2;
		for (q = 1; q <= nn; q = h + 1)
			if (s[q] != s[q - 1]) h = q;
			else {
				for (h = q; h < nn && s[h + 1] == s[q]; h++);
				ans0 += pu[h - q + 1];
			}
		printf("%lld\n", ans0);
		for (int i = 1; i <= s[nn]; i++)
			printf("%lld\n", ans[i + s[nn]]);
	}
}











  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值