【BZOJ3771】Triple 生成函数+FFT

【BZOJ3771】Triple

Description

我们讲一个悲伤的故事。

从前有一个贫穷的樵夫在河边砍柴。

这时候河里出现了一个水神,夺过了他的斧头,说:

“这把斧头,是不是你的?”

樵夫一看:“是啊是啊!”

水神把斧头扔在一边,又拿起一个东西问:

“这把斧头,是不是你的?”

樵夫看不清楚,但又怕真的是自己的斧头,只好又答:“是啊是啊!”

水神又把手上的东西扔在一边,拿起第三个东西问:

“这把斧头,是不是你的?”

樵夫还是看不清楚,但是他觉得再这样下去他就没法砍柴了。

于是他又一次答:“是啊是啊!真的是!”

水神看着他,哈哈大笑道:

“你看看你现在的样子,真是丑陋!”

之后就消失了。

樵夫觉得很坑爹,他今天不仅没有砍到柴,还丢了一把斧头给那个水神。

于是他准备回家换一把斧头。

回家之后他才发现真正坑爹的事情才刚开始。

水神拿着的的确是他的斧头。

但是不一定是他拿出去的那把,还有可能是水神不知道怎么偷偷从他家里拿走的。

换句话说,水神可能拿走了他的一把,两把或者三把斧头。

樵夫觉得今天真是倒霉透了,但不管怎么样日子还得过。

他想统计他的损失。

樵夫的每一把斧头都有一个价值,不同斧头的价值不同。总损失就是丢掉的斧头价值和。

他想对于每个可能的总损失,计算有几种可能的方案。

注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)视为一种方案。拿走三把斧头时,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)视为一种方案。

Input

第一行是整数N,表示有N把斧头。

接下来n行升序输入N个数字Ai,表示每把斧头的价值。

Output

若干行,按升序对于所有可能的总损失输出一行x y,x为损失值,y为方案数。

Sample Input

4
4
5
6
7

Sample Output

4 1
5 1
6 1
7 1
9 1
10 1
11 2
12 1
13 1
15 1
16 1
17 1
18 1
样例解释
11有两种方案是4+7和5+6,其他损失值都有唯一方案,例如4=4,5=5,10=4+6,18=5+6+7.

HINT

所有数据满足:Ai<=40000

题解:当年以为这就是个桶,后来得知这玩意叫生成函数。

设所有斧头的生成函数为x,那么我们将x自乘1,2,3次,得到x,y,z,那么考虑每种情况被计算的次数。

x——a:1次
y——aa:1次,ab:2次
z——aaa:1次,aab:3次,abc:6次

那就把aa,aaa也求出来,用aa*b-aaa得到aab,就全统计出来了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
struct cp
{
	double x,y;
	cp (double a,double b){x=a,y=b;}
	cp (){}
	cp operator + (cp a){return cp(x+a.x,y+a.y);}
	cp operator - (cp a){return cp(x-a.x,y-a.y);}
	cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
	cp operator * (double a){return cp(x*a,y*a);}
}n1[1<<19],n2[1<<19],n3[1<<19];
int n,m,top,len;
int ans[1<<19];
ll s[1<<19];
void FFT(cp *a,int f)
{
	int i,j,k,h;
	cp t;
	for(i=k=0;i<len;i++)
	{
		if(i>k)	swap(a[i],a[k]);
		for(j=(len>>1);(k^=j)<j;j>>=1);
	}
	for(h=2;h<=len;h<<=1)
	{
		cp wn(cos(f*2*pi/h),sin(f*2*pi/h));
		for(j=0;j<len;j+=h)
		{
			cp w(1,0);
			for(k=j;k<j+h/2;k++)	t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn;
		}
	}
}
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,a;
	for(i=1;i<=n;i++)	a=rd(),n1[a].x+=1,s[a]++,n2[a<<1].x+=1,n3[a*3].x+=1,m=max(m,a);
	for(len=1;len<=m*3;len<<=1);
	FFT(n1,1),FFT(n2,1),FFT(n3,1);
	for(i=0;i<len;i++)
	{
		cp x=n1[i],y=n2[i],z=n3[i];
		n2[i]=((x*x)-y)*(1.0/2),n3[i]=((x*x*x)-(x*y*3.0)+(z*2.0))*(1.0/6);
	}
	FFT(n2,-1),FFT(n3,-1);
	for(i=0;i<len;i++)	s[i]+=(ll)(n2[i].x/len+0.1)+(ll)(n3[i].x/len+0.1);
	for(i=0;i<len;i++)	if(s[i])	ans[++top]=i;
	for(i=1;i<=top;i++)	printf("%d %lld\n",ans[i],s[ans[i]]);
	return 0;
}
时间: 2024-11-10 08:43:24

【BZOJ3771】Triple 生成函数+FFT的相关文章

The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力)

The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力) 传送门:https://nanti.jisuanke.com/t/41400 题意: 给你三个数组a,b,c,要你求有多少个三元组(i,j,k),使得 \[ \begin{array}{l}{\left|A_{i}-B_{j}\right| \leq C_{k}, \text { and }} \\ {\left|B_{j}-C_{k}\right| \leq

bzoj3771 Triple

3771: Triple Time Limit: 20 Sec  Memory Limit: 64 MB Submit: 313  Solved: 174 [Submit][Status][Discuss] Description 我们讲一个悲伤的故事. 从前有一个贫穷的樵夫在河边砍柴. 这时候河里出现了一个水神,夺过了他的斧头,说: "这把斧头,是不是你的?" 樵夫一看:"是啊是啊!" 水神把斧头扔在一边,又拿起一个东西问: "这把斧头,是不是你的?&q

SPOJ - TSUM Triple Sums FFT+容斥

Triple Sums You're given a sequence s of N distinct integers.Consider all the possible sums of three integers from the sequence at three different indicies.For each obtainable sum output the number of different triples of indicies that generate it.Co

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

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

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

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

CF528D Fuzzy Search (生成函数+FFT)

题目传送门 题目大意:给你两个只包含A,G,C,T的字符串$S$,$T$,$S$长$T$短,按照如下图方式匹配 解释不明白直接上图 能容错的距离不超过$K$,求能$T$被匹配上的次数 $S$串同一个位置可以被$T$的不同位置匹配多次 对4种字符分别处理,假设我们现在只讨论字符A 对于字符串AGCAATTCAT,字符A的生成函数就是1001100010 题目要求距离不超过K就能匹配,把周围距离不超过$K$的位置都变成1,形成一个新串$S'$ $S$  1001100010 $S'$ 1111110

SPOJ Triple Sums(FFT+容斥原理)

# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <complex> # include <set> # includ

BZOJ 3771 Triple 母函数+FFT

题目大意:给定n个物品,可以用一个/两个/三个不同的物品凑出不同的价值,求每种价值有多少种拼凑方案(顺序不同算一种) 首先搞出这n个物品的母函数a 将a的每项的平方求和得到多项式b 将a的每项的立方求和得到多项式c 那么如果不考虑顺序和重复 那么方案数就是a+b+c 现在考虑顺序和重复后 三个物品的方案数为(a^3-3*a*b+2*c)/6 两个物品的方案数为(a^2-b)/2 一个物品的方案数为a 故最终答案为(a^3-3*a*b+2*c)/6+(a^2-b)/2+a 用FFT搞一下就好了-

【JSOI2012】 分零食 生成函数 FFT

我们构造$f(x)$的生成函数$G(x)$,那么显然$[x^k]G(x)=Ok^2+Sk+U$ 那么显然,答案即为$\sum_{i=1}^{n} [x^m]G^i(x)$ 我们构造答案的生成函数$F(x)=\sum_{i=1}^{n} G^i(x)$ 根据等比数列求和公式,$F(x)=\dfrac{1-G(x)}{1-G^{A+1}(x)}$ 如果去等比数列求和的话,你需要多项式快速幂+多项式求逆,时间复杂度显然是$O(m\ log\ m)$的. 然而这个模数并不是质数,所以这么搞不是很好搞.