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

题目传送门

给你一个长度为$n$的自然数序列$a$,定义一段区间的权值为这一段区间里所有数的和,分别输出权值为$[0,\sum a_{i}]$的区间的长度之和

想到了生成函数的话,这道题并不难做。但很多细节真是不太好搞

我们首先预处理出前缀和s,那么一段区间$[l,r]$的权值就是$s_{r}-s_{l-1}$

容易联想到卷积

第一个多项式是 区间右端点的前缀和 作为指数的生成函数,每一项的系数是 右端点的编号之和

第二个多项式是 区间左端点的前缀和 作为指数的生成函数,每一项的系数是 左端点的编号之和

然而区间长度是相减而不是相乘

我们可以把问题转化成 右端点编号$*1$-左端点编号$*1$,求两次卷积再相减即可

然而左端点的前缀和是负数,我们把生成函数整体右移

然而序列里还有$0$的情况

如果序列里出现了连续的$0$,我们发现这部分答案我们无法通过卷积统计

因为按照我们的方法,在多项式对应的相同的位置卷积的话,两次统计的答案就被减掉了

所以连续的$0$对答案的影响通过$O(n)$扫一遍统计

每新加入一个新的$0$,就会多产生一个等差数列的贡献

另外答案比较大,$FFT$需要开$long\;double$

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define N1 (1<<18)
  6 #define M1 (N1<<1)
  7 #define il inline
  8 #define dd double
  9 #define ld long double
 10 #define ll long long
 11 using namespace std;
 12
 13 int T,n;
 14
 15 int gint()
 16 {
 17     int ret=0,fh=1;char c=getchar();
 18     while(c<‘0‘||c>‘9‘){if(c==‘-‘)fh=-1;c=getchar();}
 19     while(c>=‘0‘&&c<=‘9‘){ret=ret*10+c-‘0‘;c=getchar();}
 20     return ret*fh;
 21 }
 22
 23 const ld pi=acos(-1);
 24 struct cp{
 25 ld x,y;
 26 friend cp operator + (const cp &s1,const cp &s2){ return (cp){s1.x+s2.x,s1.y+s2.y}; }
 27 friend cp operator - (const cp &s1,const cp &s2){ return (cp){s1.x-s2.x,s1.y-s2.y}; }
 28 friend cp operator * (const cp &s1,const cp &s2){ return (cp){s1.x*s2.x-s1.y*s2.y,s1.y*s2.x+s1.x*s2.y}; }
 29 }a[N1],b[N1],c[N1];
 30 int r[N1];
 31 void FFT(cp *s,int len,int type)
 32 {
 33     int i,j,k; cp wn,w,t;
 34     for(i=0;i<len;i++) if(i<r[i]) swap(s[i],s[r[i]]);
 35     for(k=2;k<=len;k<<=1)
 36     {
 37         wn=(cp){cos(pi*2.0*type/k),sin(pi*2.0*type/k)};
 38         for(i=0;i<len;i+=k)
 39         {
 40             w=(cp){1,0};
 41             for(j=0;j<(k>>1);j++,w=w*wn)
 42             {
 43                 t=w*s[i+j+(k>>1)];
 44                 s[i+j+(k>>1)]=s[i+j]-t;
 45                 s[i+j]=s[i+j]+t;
 46             }
 47         }
 48     }
 49 }
 50 void FFT_Main(int len)
 51 {
 52     FFT(a,len,1); FFT(b,len,1);
 53     for(int i=0;i<len;i++) c[i]=a[i]*b[i];
 54     FFT(c,len,-1);
 55     for(int i=0;i<len;i++) c[i].x/=len;
 56 }
 57
 58 int v[N1],s[N1];
 59 ll ans[N1];
 60
 61 int main()
 62 {
 63     scanf("%d",&T);
 64     while(T--) {
 65
 66     memset(v,0,sizeof(v)); memset(s,0,sizeof(s)); memset(r,0,sizeof(r)); memset(ans,0,sizeof(ans));
 67     int i,j,maxn=0,len,L,num;
 68     scanf("%d",&n);
 69     for(i=1;i<=n;i++) v[i]=gint(), s[i]=s[i-1]+v[i], maxn=max(maxn,s[i]);
 70     if(!maxn)
 71     {
 72         for(i=1;i<=n;i++)
 73             ans[0]+=1ll*(i+1)*i/2;
 74         printf("%lld\n",ans[0]);
 75         continue;
 76     }
 77
 78     for(len=1,L=0;len<maxn+maxn+1;len<<=1,L++);
 79     for(i=0;i<len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
 80
 81     memset(a,0,sizeof(a)); memset(b,0,sizeof(b));
 82     for(i=0;i<=n;i++) a[s[i]].x+=i;
 83     for(i=0;i<=n;i++) b[-s[i]+maxn].x++;
 84     FFT_Main(len);
 85     for(i=maxn;i<=(maxn<<1);i++) ans[i-maxn]=(ll)(c[i].x+0.5);
 86
 87     memset(a,0,sizeof(a)); memset(b,0,sizeof(b));
 88     for(i=0;i<=n;i++) a[s[i]].x++;
 89     for(i=0;i<=n;i++) b[-s[i]+maxn].x+=i;
 90     FFT_Main(len);
 91     for(i=maxn;i<=(maxn<<1);i++) ans[i-maxn]-=(ll)(c[i].x+0.5);
 92
 93     for(i=1,num=0;i<=n;i++)
 94         if(!v[i]){ num++; ans[0]+=1ll*(num+1)*(num)/2ll; }
 95         else{ num=0; }
 96     for(i=0;i<=maxn;i++) printf("%lld\n",ans[i]);
 97     //puts("");
 98     }
 99     return 0;
100
101 }

原文地址:https://www.cnblogs.com/guapisolo/p/10353204.html

时间: 2024-10-10 10:01:39

HDU 5307 He is Flying (生成函数+FFT)的相关文章

【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个数(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 namesp

hdu 1402 大数A*B模板(FFT)

hdu 1402 大数A*B模板(FFT) 题目链接 参考博客 #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <complex> #include <iostream> using namespace std; typedef long long ll; template <class T> void

HDU 5515 Game of Flying Circus 二分

Game of Flying Circus Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5515 Description The discovery of anti-gravitations technology changed the world. The invention of anti-gravitation shoes (Grav-shoes) enable

【BZOJ3771】Triple 生成函数+FFT

[BZOJ3771]Triple Description 我们讲一个悲伤的故事. 从前有一个贫穷的樵夫在河边砍柴. 这时候河里出现了一个水神,夺过了他的斧头,说: “这把斧头,是不是你的?” 樵夫一看:“是啊是啊!” 水神把斧头扔在一边,又拿起一个东西问: “这把斧头,是不是你的?” 樵夫看不清楚,但又怕真的是自己的斧头,只好又答:“是啊是啊!” 水神又把手上的东西扔在一边,拿起第三个东西问: “这把斧头,是不是你的?” 樵夫还是看不清楚,但是他觉得再这样下去他就没法砍柴了. 于是他又一次答:“

HDU - 1402 A * B Problem Plus FFT裸题

http://acm.hdu.edu.cn/showproblem.php?pid=1402 题意: 求$a*b$ 但是$a$和$b$的范围可以达到 $1e50000$ 题解: 显然...用字符串模拟的大数或者压位的大数是无法胜任这种计算的.... 然后,2个大整数相乘,可以理解为卷积,所以就用快速傅里叶变换(FFT)来加速他 模板题 简单总结一下对FFT的认知: FFT用于算卷积,卷积可以理解为两个多项式相乘显然复杂度是$O(n^2)$的 但是fft可以优化为$O(nlogn)$如何优化,考虑

hdu 1521 排列组合 —— 指数型生成函数

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1521 标准的指数型生成函数: WA了好几遍,原来是多组数据啊囧: 注意精度,直接强制转换(int)是舍去小数,会WA,+0.5再强制转换或输出 %.0lf 是四舍五入,能A. 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace

UVA 12633 Super Rooks on Chessboard (生成函数+FFT)

题面传送门 题目大意:给你一张网格,上面有很多骑士,每个骑士能横着竖着斜着攻击一条直线上的格子,求没被攻击的格子的数量总和 好神奇的卷积 假设骑士不能斜着攻击 那么答案就是没被攻击的 行数*列数 接下来考虑斜着攻击对答案的贡献 以左下角为坐标原点建立坐标系,发现一条对角线的点的$(x+y)$坐标是相同的 考虑卷积,设计两个生成函数$a,b$ 如果第i行没骑士,则$a_{i}=1$,反之为$0$ 如果第i列没骑士,则$b_{i}=1$,反之为$0$ 我们对两个式子进行卷积,可以求出每一条对角线上还