題目大意:給出 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;
}
總結
以上是生活随笔 為你收集整理的中石油训练赛 - Cafebazaar’s Chess Tournament(FFT) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。