hdu 5307 He is Flying(推公式+FFT)

题意:总区间中有n个数(n<=100000),求每个区间和所对应的区间长度(j-i+1)和;

思路:母函数求得多项式为(∑ix^si)(∑x−^si−1)−(∑x^si)(∑(i−1)x−^si−1);

用FFT求出多项式,由于精度范围(s=50000),需要使用long double;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define ld long double
ld pi=acos(-1.0);   //注意π的精度
int num[500010],sum[500010];
long long snum[500010],ans[500010];
long long p,res;
struct Complex{
   ld r;
   ld i;
   Complex(){};
   Complex(ld a,ld b){
       r=a;i=b;
   }
   Complex operator +(const Complex &t)const{
      return Complex(r+t.r,i+t.i);
   }
   Complex operator -(const Complex &t)const{
      return Complex(r-t.r,i-t.i);
   }
   Complex operator *(const Complex &t)const{
      return Complex(r*t.r-i*t.i,r*t.i+i*t.r);
   }
}a1[500010],a2[500010];
void fft(Complex y[],int n,int rev){
    for(int i=1,j,k,t;i<n;i++){
        for(j=0,k=n>>1,t=i;k;k>>=1,t>>=1) j=j<<1|t&1;
        if(i<j) swap(y[i],y[j]);
    }
    for(int s=2,ds=1;s<=n;ds=s,s<<=1){
      Complex wn=Complex(cos(rev*2*pi/s),sin(rev*2*pi/s)),w=Complex(1,0),t;
      for(int k=0;k<ds;k++,w=w*wn){
        for(int i=k;i<n;i+=s){
            t=w*y[i+ds];
            y[i+ds]=y[i]-t;
            y[i]=y[i]+t;
        }
      }
    }
    if(rev==-1) for(int i=0;i<n;i++) y[i].r/=n;
}
int main()
{
    int i,j,k,t,n;
    snum[0]=0;
    for(long long i=1;i<=100000;i++)
        snum[i]=snum[i-1]+i*(i+1)/2;  //区间长度
    scanf("%d",&t);
    while(t--){
        memset(sum,0,sizeof(sum));
        scanf("%d",&n);
        for(i=0;i<n;i++){
            scanf("%d",&num[i]);
        }
        sum[0]=num[0];
        for(i=1;i<n;i++){
           sum[i]=sum[i-1]+num[i];  //区间和
        }
        p=0;res=0;
        for(i=0;i<n;i++){
          if(num[i]==0){
             p++;
          }
          else{
            res+=snum[p];
             p=0;
          }
        }
        res+=snum[p];
        printf("%lld\n",res);  //预处理区间和为0的情况
        int total=sum[n-1];    //多项式长度
        int total2=total*2;
        memset(a1,0,sizeof(a1));
        memset(a2,0,sizeof(a2));
        int len=1;
        while(len<=total2) len<<=1;
        for(i=0;i<n;i++){       //构造多项式
            a1[sum[i]].r+=i+1;
            if(i!=n-1)
                a2[total-sum[i]].r+=1;
        }
        a2[total].r+=1;
        fft(a1,len,1);
        fft(a2,len,1);
        for(i=0;i<=len;i++) a1[i]=a1[i]*a2[i];
        fft(a1,len,-1);
        for(i=0;i<=len;i++) ans[i]=(long long)(a1[i].r+0.5);

        memset(a1,0,sizeof(a1));
        memset(a2,0,sizeof(a2));
        for(i=0;i<n;i++){    //构造多项式
           a1[sum[i]].r+=1;
           if(i!=n-1){
             a2[total-sum[i]].r+=i+1;
           }
        }
        fft(a1,len,1);
        fft(a2,len,1);
        for(i=0;i<=len;i++) a1[i]=a1[i]*a2[i];
        fft(a1,len,-1);
        for(i=0;i<=len;i++) ans[i]-=(long long)(a1[i].r+0.5);
        for(i=total+1;i<=total2;i++)  //结果
            printf("%lld\n",ans[i]);
    }
    return 0;
}
时间: 2024-08-01 07:34:09

hdu 5307 He is Flying(推公式+FFT)的相关文章

hdu 6128 Inverse of sum(推公式)

题目链接:hdu 6128 Inverse of sum 题意: 给你n个数,问你有多少对i,j,满足i<j,并且1/(ai+aj)=1/ai+1/aj 在%p意义下. 题解: 不愧是高中生,推公式神题. 将式子通分化简后可得(ai2+aj2+ai*aj)%p=0. 然后两边同时将两边乘(ai-aj),化简可得(ai3-aj3)%p=0. 然后就可以用map记录一下个数,并且减掉ai==aj时不合法的情况就行了. 1 #include<bits/stdc++.h> 2 #define F

2017多校第7场 HDU 6128 Inverse of sum 推公式或者二次剩余

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6128 题意:给你n个数,问你有多少对i,j,满足i<j,并且1/(ai+aj)=1/ai+1/aj 在%p意义下. 解法:官方题解说是用二次剩余来解,但是我并不会这玩意了.在网上看到一位大佬没有二次剩余直接通过推公式做出了这题,真是神奇.http://www.cnblogs.com/bin-gege/p/7367337.html  将式子通分化简后可得(ai2+aj2+ai*aj)%p=0 .然后两

【FFT】HDU 5307 He is Flying

通道:http://acm.hdu.edu.cn/showproblem.php?pid=5307 题意:给出n个数,然后求所有的dp[i],这里的dp[i]就是所有连续区间内和为i的区间长度总和 代码: 感谢沈洋. 1 #include <cstdio> 2 3 typedef long long ll; 4 5 const int N = 262145, BUF_SIZE = N * 10, BF_LIM = 16; 6 7 namespace BF { 8 void mul(ll *a,

FFT(快速傅里叶变换):HDU 5307 He is Flying

有必要吐槽一下此题的英文水平. FFT强撸,没有什么难点. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cmath> 5 using namespace std; 6 const long double PI=acos(-1.0); 7 const int maxn=300010; 8 struct complex{ 9 long double r,i;

HDU 5307 He is Flying (生成函数+FFT)

题目传送门 给你一个长度为$n$的自然数序列$a$,定义一段区间的权值为这一段区间里所有数的和,分别输出权值为$[0,\sum a_{i}]$的区间的长度之和 想到了生成函数的话,这道题并不难做.但很多细节真是不太好搞 我们首先预处理出前缀和s,那么一段区间$[l,r]$的权值就是$s_{r}-s_{l-1}$ 容易联想到卷积 第一个多项式是 区间右端点的前缀和 作为指数的生成函数,每一项的系数是 右端点的编号之和 第二个多项式是 区间左端点的前缀和 作为指数的生成函数,每一项的系数是 左端点的

HDU dice DP求期望+推公式

题意: 一个m边形的骰子,求连续投出n个相同的面,和m个两两不同的面的期望次数. solution: 令\(f_i\)表示已经连续投出i个相同的面,到连续投出n个还需要的期望次数. 令\(g_i\)类似的表示第二种问题的期望次数. 对于\(f_i\) ,有两种情况: ① 投出了和前i个相同的面,转移到了\(f_{i+1}\) ,那么\(f_i+=(f_{i+1}+1)*\frac{1}{m}\) ② 投出了一个不同的面,转移到了\(f_1\),那么\(f_i+=(f_1+1)*\frac{m-1

HDU 2298:Toxophily(推公式)

http://acm.hdu.edu.cn/showproblem.php?pid=2298 题意:给出一个x,y,v,问从(0,0)以v为初速度射箭,能否射到(x,y)这个点,如果能,输出最小的射出角度(与x轴),否则输出-1. 思路:首先考虑不能到达的情况,由动能定理mgy > 1 / 2 * m * v * v的时候,就输出-1. 然后可以列出两个式子: x = v * t * cos(θ)  ① y = v * t * sin(θ) - 1 / 2 * g * t * t. ② 把①带入

HDU 4873 ZCC Loves Intersection(JAVA、大数、推公式)

在一个D维空间,只有整点,点的每个维度的值是0~n-1 .现每秒生成D条线段,第i条线段与第i维度的轴平行.问D条线段的相交期望. 生成线段[a1,a2]的方法(假设该线段为第i条,即与第i维度的轴平行)为,i!=j时,a1[j]=a2[j],且随机取区间[0,n-1]内的整数.然后a1[i],a2[i]在保证a1[i]<a2[i]的前提下同样随机. 由于D条线段各自跟自己维度的轴平行,我们可以转换成只求第i个维度与第j个维度的相交期望,然后乘以C(2,n)就好了 显然线段[a1,a2]和线段[

HDU 4870 Rating(概率、期望、推公式) &amp;&amp; ZOJ 3415 Zhou Yu

其实zoj 3415不是应该叫Yu Zhou吗...碰到ZOJ 3415之后用了第二个参考网址的方法去求通项,然后这次碰到4870不会搞.参考了chanme的,然后重新把周瑜跟排名都反复推导(不是推倒)四五次才上来写这份有抄袭嫌疑的题解... 这2题很类似,多校的rating相当于强化版,不过原理都一样.好像是可以用高斯消元做,但我不会.默默推公式了. 公式推导参考http://www.cnblogs.com/chanme/p/3861766.html#2993306 http://www.cn