題目大意:給出 n 個(gè)隊(duì)伍,每個(gè)隊(duì)伍有兩個(gè)屬性分別記為 a 和 b ,如果隊(duì)伍 i 和隊(duì)伍 j 比賽:
a[ i ] > a[ j ] && b[ i ] > b[ j ] :i 隊(duì)伍獲勝 a[ j ] > a[ i ] && b[ j ] > b[ i ] :j 隊(duì)伍獲勝 其余情況全部平局
獲勝加一分,平局加 0.5 分,失敗不扣分
現(xiàn)在有一個(gè)新加入的隊(duì)伍,讓這個(gè)隊(duì)伍和已有的 n 支隊(duì)伍互相比賽,該隊(duì)伍的屬性可以在比賽前設(shè)置為 n 支隊(duì)伍中未出現(xiàn)過的任意實(shí)數(shù),現(xiàn)在問該隊(duì)伍能有多少種不同的得分結(jié)果
題目分析:首先轉(zhuǎn)換模型,將兩個(gè)屬性映射到 x 軸和 y 軸上,然后一個(gè)隊(duì)伍扔到二維平面坐標(biāo)系上,會出現(xiàn)這種情況:
因?yàn)轭}目中約束了新加入隊(duì)伍的屬性不能與原有隊(duì)伍的屬性值相同,換句話說新加入的隊(duì)伍只能出現(xiàn)在四個(gè)方格中,而不會出現(xiàn)在藍(lán)色的直線或交點(diǎn)上,因?yàn)槠骄趾捅貏僦g的加分是兩倍的關(guān)系,所以可以令其相互獨(dú)立,語言描述的話就是平行于 y 軸的直線右側(cè)可以加 0.5 分,平行于 x 軸的直線上側(cè)可以加 0,5 分,大概下圖的樣子:
然后將 n 個(gè)互不相同的點(diǎn)都扔到二維平面坐標(biāo)系中,就會出現(xiàn) n 條平行于 x 軸的直線和 n 條平行于 y 軸的直線,大概會交出很多很多的方格:
現(xiàn)在討論一下,如果新加入的隊(duì)伍位于紅色方格時(shí)的貢獻(xiàn):
其下方有兩條直線,可以得到 0.5 * 2 分 其左側(cè)有兩條直線,可以得到 0.5 * 2 分
總共可以得到兩分,對于每個(gè)集合都可以這樣計(jì)算一個(gè)分?jǐn)?shù),最后我們只需要對所有的方格內(nèi)的數(shù)去重就是答案了
不難看出上述模型實(shí)質(zhì)上就是對屬性 a 進(jìn)行排序后求個(gè)前綴和,對屬性 b 排序后求個(gè)前綴和,然后從 a 的前綴和中遍歷所有元素,與 b 的前綴和中的所有元素分別加和計(jì)算,共有 a * b 個(gè)答案,去重后就是答案
現(xiàn)在得到了一個(gè)樸素的 n * n 的模型,考慮優(yōu)化
設(shè)屬性 a 的前綴和的元素為集合 A ,屬性 b 的前綴和的元素為集合 B,A 中的元素分別為 A[ 0 ] ~ A[ n ] ,B 中的元素分別為 B[ 0 ] ~ B[ m ] ,考慮多項(xiàng)式卷積,令?,,在原模型中對應(yīng)的元素相加,即 A[ x ] + B[ y ] ,在多項(xiàng)式卷積中對應(yīng),考慮系數(shù)都為 1 的話,得到的也將是,換句話說,在進(jìn)行卷積之后的答案多項(xiàng)式,系數(shù)非零的位置對應(yīng)的也就是上述模型去重之后的答案
直接卷積時(shí)間復(fù)雜度也是 n * m 級別的,考慮 FFT 優(yōu)化,套個(gè)板子就好了,時(shí)間復(fù)雜度 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;
}
總結(jié)
以上是生活随笔 為你收集整理的中石油训练赛 - Cafebazaar’s Chess Tournament(FFT) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。