给出三组边A,B,C,从三组边中各选一条边,问有多少可以种做法可以组成三角形
总共边的组合减去不能组成三角形的组合数就是答案。对于每组边种的每条边,统计剩余的两组边的和有多少对和大小小于自己,就是不合法的数量。对于剩余两组边的和的权值数组,在n<=1000的时候暴力统计,n>1000的时候生成函数FFT统计
#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());
}
}