2019上海网络赛 Triple (暴力+生成函数&FFT)

2019上海网络赛 Triple (暴力+生成函数&FFT)

题目大意

给出三组边A,B,C,从三组边中各选一条边,问有多少可以种做法可以组成三角形

解题思路

总共边的组合减去不能组成三角形的组合数就是答案。对于每组边种的每条边,统计剩余的两组边的和有多少对和大小小于自己,就是不合法的数量。对于剩余两组边的和的权值数组,在n<=1000的时候暴力统计,n>1000的时候生成函数FFT统计

AC代码

#include
using namespace std;
const int size=2e5+5;
typedef long long LL;
int a[size],b[size],c[size];
int cnta[size],cntb[size],cntc[size];
LL sum[size];
int n;
namespace Polynomial_multiplication{
	const double pi=acos(-1);
	typedef complex<double> cp;
	int n, m, rev[size << 2];
	cp a[size << 2], b[size << 2];
	void init(int len) {
		for (n = 1, m = 0; n <= len; n <<= 1, m++);
		for (int i = 0; i < n; ++i) {
			rev[i] = rev[i >> 1] >> 1 | (i & 1) << (m - 1);
			a[i] = cp(0, 0);
			b[i] = cp(0, 0);
		}
	}
	void builda(vector<int> x,int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
	void builda(int x[],int len){for(int i=0;i<=len;i++) a[i]=cp(x[i],0);}
	void buildb(vector<int> x,int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
	void buildb(int x[],int len){for(int i=0;i<=len;i++) b[i]=cp(x[i],0);}
	void fft(cp *a, int f) {
		for (int i = 0; i < n; ++i)if (i < rev[i])swap(a[i], a[rev[i]]);
		for (int i = 1; i < n; i <<= 1) {
			double alpha = pi / i;
			if (f == -1)alpha = -pi / i;
			for (int k = 0; k < i; ++k) {
				cp w = cp(cos(alpha*k), sin(alpha*k));
				for (int j = k; j < n; j += (i << 1)) {
					cp x = w * a[j + i];
					a[j + i] = a[j] - x;
					a[j] += x;
				}
			}
		}
		if(f==-1) for(int i=0;i<n;i++) a[i]/=n;
	}
	void calc(vector<int> &v,int len) {
		fft(a, 1); fft(b, 1);
		for (int i = 0; i < n; ++i)a[i] *= b[i];
		fft(a, -1);
		for(int i=0;i<=len;i++) v.push_back(LL(a[i].real()+0.5));
	}
	void calc(LL v[],int len){
		fft(a,1),fft(b,1);
		for(int i=0;i<n;i++) a[i]*=b[i];
		fft(a,-1);
		for(int i=0;i<=len;i++) v[i]=LL(a[i].real()+0.5);
	}
} 
inline LL bruce(int x[],int y[],int z[])
{
	LL ans=0;
	for(int i=1;i<=z[n];i++) sum[i]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			sum[x[i]+y[j]]++;
		}
	}
	for(int i=1;i<=z[n];i++)
	{
		sum[i]=sum[i]+sum[i-1];
	}
	for(int i=1;i<=n;i++)
	{
		ans+=sum[z[i]-1];
	}
	return ans;
}
inline LL quick_solve(int x[],int y[],int z[])
{
	Polynomial_multiplication::init(2*z[n]+1);
	Polynomial_multiplication::builda(x,z[n]);
	Polynomial_multiplication::buildb(y,z[n]);
	Polynomial_multiplication::calc(sum,z[n]);
	for(int i=1;i<=z[n];i++)
	{
		sum[i]=sum[i]+sum[i-1];
	}
	LL ans=0;
	for(int i=1;i<=n;i++) ans+=sum[z[i]-1];
	return ans;
}
LL solve()
{
	LL  ans=1LL*n*n*n;
	if(n<=1000)
		return ans-bruce(a,b,c)-bruce(a,c,b)-bruce(b,c,a);	
	else 
	{
		memset(cnta,0,sizeof(cnta));
		memset(cntb,0,sizeof(cntb));
		memset(cntc,0,sizeof(cntc));
		for(int i=1;i<=n;i++)
		{
			cnta[a[i]]++;
			cntb[b[i]]++;
			cntc[c[i]]++;
		}
		return ans-quick_solve(cnta,cntb,c)-quick_solve(cntb,cntc,a)-quick_solve(cnta,cntc,b);
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	for(int kace=1;kace<=t;kace++)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);sort(a+1,a+1+n);
		for(int i=1;i<=n;i++) scanf("%d",&b[i]);sort(b+1,b+1+n);
		for(int i=1;i<=n;i++) scanf("%d",&c[i]);sort(c+1,c+1+n);
		printf("Case #%d: %lld\n",kace,solve());
	}
}

你可能感兴趣的:(多项式)