题目大意:给出 n 个队伍,每个队伍有两个属性分别记为 a 和 b ,如果队伍 i 和队伍 j 比赛:
- a[ i ] > a[ j ] && b[ i ] > b[ j ] :i 队伍获胜
- a[ j ] > a[ i ] && b[ j ] > b[ i ] :j 队伍获胜
- 其余情况全部平局
获胜加一分,平局加 0.5 分,失败不扣分
现在有一个新加入的队伍,让这个队伍和已有的 n 支队伍互相比赛,该队伍的属性可以在比赛前设置为 n 支队伍中未出现过的任意实数,现在问该队伍能有多少种不同的得分结果
题目分析:首先转换模型,将两个属性映射到 x 轴和 y 轴上,然后一个队伍扔到二维平面坐标系上,会出现这种情况:
因为题目中约束了新加入队伍的属性不能与原有队伍的属性值相同,换句话说新加入的队伍只能出现在四个方格中,而不会出现在蓝色的直线或交点上,因为平局和必胜之间的加分是两倍的关系,所以可以令其相互独立,语言描述的话就是平行于 y 轴的直线右侧可以加 0.5 分,平行于 x 轴的直线上侧可以加 0,5 分,大概下图的样子:
然后将 n 个互不相同的点都扔到二维平面坐标系中,就会出现 n 条平行于 x 轴的直线和 n 条平行于 y 轴的直线,大概会交出很多很多的方格:
现在讨论一下,如果新加入的队伍位于红色方格时的贡献:
- 其下方有两条直线,可以得到 0.5 * 2 分
- 其左侧有两条直线,可以得到 0.5 * 2 分
总共可以得到两分,对于每个集合都可以这样计算一个分数,最后我们只需要对所有的方格内的数去重就是答案了
不难看出上述模型实质上就是对属性 a 进行排序后求个前缀和,对属性 b 排序后求个前缀和,然后从 a 的前缀和中遍历所有元素,与 b 的前缀和中的所有元素分别加和计算,共有 a * b 个答案,去重后就是答案
现在得到了一个朴素的 n * n 的模型,考虑优化
设属性 a 的前缀和的元素为集合 A ,属性 b 的前缀和的元素为集合 B,A 中的元素分别为 A[ 0 ] ~ A[ n ] ,B 中的元素分别为 B[ 0 ] ~ B[ m ] ,考虑多项式卷积,令 ,,在原模型中对应的元素相加,即 A[ x ] + B[ y ] ,在多项式卷积中对应,考虑系数都为 1 的话,得到的也将是,换句话说,在进行卷积之后的答案多项式,系数非零的位置对应的也就是上述模型去重之后的答案
直接卷积时间复杂度也是 n * m 级别的,考虑 FFT 优化,套个板子就好了,时间复杂度 nlogn
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
#include<complex>
#define cp complex < double >
using namespace std;
const double pi=acos(-1);
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
int cnt1[N],cnt2[N];
bool vis1[N],vis2[N];
int lena=0,lenb=0,n,res[N<<2];
cp F[N<<2],G[N<<2],arr[N<<2],inv[N<<2];
void init()
{
for (int i=0;i<n;i++)
{
arr[i]=cp(cos(2*pi*i/n),sin(2*pi*i/n));
inv[i]=conj(arr[i]);
}
}
void FFT(cp *a,cp *arr)
{
int lim=0;
while ((1<<lim)<n) lim++;
for (int i=0;i<n;i++)
{
int t=0;
for (int j=0;j<lim;j++)
if ((i>>j) & 1) t|=1<<(lim-j-1);
if (i<t) swap(a[i],a[t]);
}
for (int l=2;l<=n;l*=2)
{
int m=l/2;
for (cp *buf=a;buf!=a+n;buf+=l)
for (int i=0;i<m;i++)
{
cp t=arr[n/l*i]*buf[i+m];
buf[i+m]=buf[i]-t;
buf[i]+=t;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
cnt1[a]++,cnt2[b]++;
}
int sum=0;
vis1[sum]=true;
for(int i=1;i<N;i++)
{
if(!cnt1[i])
continue;
sum+=cnt1[i];
vis1[sum]=true;
}
sum=0;
vis2[sum]=true;
for(int i=1;i<N;i++)
{
if(!cnt2[i])
continue;
sum+=cnt2[i];
vis2[sum]=true;
}
for(int i=0;i<N;i++)
{
if(vis1[i])
lena=i+1;
if(vis2[i])
lenb=i+1;
F[i].real(vis1[i]);
G[i].real(vis2[i]);
}
n=1;
while(n<(lena+lenb))
n<<=1;
init();
FFT(F,arr);FFT(G,arr);
for(int i=0;i<n;i++)
F[i]*=G[i];
FFT(F,inv);
for(int i=0;i<n;i++)
res[i]=floor(F[i].real()/n+0.5);
int ans=0;
for(int i=0;i<lena+lenb-1;i++)
ans+=!!res[i];
printf("%d\n",ans);
return 0;
}