日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

牛客网-数据结构笔试题目(二)-万万没想到之抓捕孔连顺思路解析(附源码)

發(fā)布時(shí)間:2023/12/2 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 牛客网-数据结构笔试题目(二)-万万没想到之抓捕孔连顺思路解析(附源码) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題意

我叫王大錘,是一名特工。我剛剛接到任務(wù):在字節(jié)跳動(dòng)大街進(jìn)行埋伏,抓捕恐怖分子孔連順。和我一起行動(dòng)的還有另外兩名特工,我提議

我們?cè)谧止?jié)跳動(dòng)大街的N個(gè)建筑中選定3個(gè)埋伏地點(diǎn)。

為了相互照應(yīng),我們決定相距最遠(yuǎn)的兩名特工間的距離不超過(guò)D。

我特喵是個(gè)天才! 經(jīng)過(guò)精密的計(jì)算,我們從X種可行的埋伏方案中選擇了一種。這個(gè)方案萬(wàn)無(wú)一失,顫抖吧,孔連順!

……

萬(wàn)萬(wàn)沒(méi)想到,計(jì)劃還是失敗了,孔連順化妝成小龍女,混在cosplay的隊(duì)伍中逃出了字節(jié)跳動(dòng)大街。只怪他的偽裝太成功了,就是楊過(guò)本人來(lái)了也發(fā)現(xiàn)不了的!

請(qǐng)聽(tīng)題:給定N(可選作為埋伏點(diǎn)的建筑物數(shù))、D(相距最遠(yuǎn)的兩名特工間的距離的最大值)以及可選建筑的坐標(biāo),計(jì)算在這次行動(dòng)中,大錘的小隊(duì)有多少種埋伏選擇。

注意:

兩個(gè)特工不能埋伏在同一地點(diǎn)

三個(gè)特工是等價(jià)的:即同樣的位置組合(A, B, C) 只算一種埋伏方法,不能因“特工之間互換位置”而重復(fù)使用

輸入描述:

第一行包含空格分隔的兩個(gè)數(shù)字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)

第二行包含N個(gè)建筑物的的位置,每個(gè)位置用一個(gè)整數(shù)(取值區(qū)間為[0, 1000000])表示,從小到大排列(將字節(jié)跳動(dòng)大街看做一條數(shù)軸)
輸出描述:
一個(gè)數(shù)字,表示不同埋伏方案的數(shù)量。結(jié)果可能溢出,請(qǐng)對(duì) 99997867 取模
輸入例子1:
4 3
1 2 3 4
輸出例子1:
4
例子說(shuō)明1:
可選方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)
輸入例子2:
5 19
1 10 20 30 50
輸出例子2:
1
例子說(shuō)明2:
可選方案 (1, 10, 20)

題解

首先我們來(lái)理一下題目當(dāng)中出現(xiàn)的幾個(gè)點(diǎn),我們要算的是特工埋伏的方法數(shù)。特工埋伏需要固定3個(gè)特工,并且要求特工之間的距離小于D。

題目當(dāng)中還說(shuō)特工之間是等價(jià)的,如果大家敏感的話(huà),這其實(shí)是一道排列組合問(wèn)題,準(zhǔn)確的說(shuō)是一道組合問(wèn)題。這和n球里任意取3個(gè)出來(lái)有多少種組合本質(zhì)上是一樣的,如果大家還記得組合公式的話(huà)套一下就可以了:

這里的k固定了是3,我們代入進(jìn)去可以簡(jiǎn)化為:
,所以現(xiàn)在唯一的問(wèn)題就是n不知道。我們可以知道n是可以通過(guò)D來(lái)進(jìn)行計(jì)算的,至于怎么計(jì)算我們先放一放,先來(lái)對(duì)問(wèn)題進(jìn)行一個(gè)建模。

首先,由于題目當(dāng)中說(shuō)的是特工分布在一條大街上,我們可以把大街看成是橫坐標(biāo)軸,每一個(gè)特工就是坐標(biāo)軸上的一個(gè)點(diǎn)。由于題目當(dāng)中限定了3個(gè)特工之間的距離不超過(guò)D,其實(shí)也就是最左側(cè)和最右側(cè)的距離小于D。我們可以想象有一個(gè)長(zhǎng)度為D的線段在這個(gè)坐標(biāo)軸上滑動(dòng),窗口內(nèi)的特工數(shù)量就是n。

由于特工的位置是固定的,所以這個(gè)n是很好算的。假如我們用一個(gè)數(shù)組sum來(lái)存儲(chǔ)從0開(kāi)始到當(dāng)前位置一共包含的特工數(shù),那么區(qū)間[x, x+D]范圍內(nèi)的特工數(shù)量其實(shí)就等于sum[x+D]-sum[x]。這里我們可以通過(guò)差分?jǐn)?shù)組來(lái)搞定,也可以使用樹(shù)狀數(shù)組,我這里用了樹(shù)狀數(shù)組,其實(shí)沒(méi)有必要,因?yàn)椴簧婕案虏僮鳌?/p>

所以接下來(lái)我們要做的就是把這個(gè)窗口從最左側(cè)往右滑動(dòng)就可以了,每次窗口內(nèi)特工變化的時(shí)候,我們計(jì)算一下構(gòu)成的組合數(shù)累加在一起就可以了。進(jìn)一步我們可以想到,其實(shí)我們只需要關(guān)心窗口的左邊緣,我們把窗口的左邊緣和特工重疊,這樣能夠使得窗口內(nèi)的特工數(shù)量盡可能多。

比如[1, 2, 3, 4]這個(gè)樣例,顯然當(dāng)窗口的左邊緣和1重疊的時(shí)候,能夠把4也覆蓋了,否則就會(huì)遺漏一些情況。很多人想到這里就去寫(xiě)代碼了,但是這里藏了一個(gè)trick,當(dāng)我們的窗口移動(dòng)的時(shí)候,實(shí)際上會(huì)出現(xiàn)有一些組合重復(fù)的情況。

比如說(shuō)窗口長(zhǎng)度是4,當(dāng)前窗口內(nèi)的特工是[1, 2, 3, 4],其中有一種組合是(2, 3, 4)。當(dāng)我們窗口左側(cè)邊緣移動(dòng)到2的時(shí)候,窗口內(nèi)的特工是[2, 3, 4, 5],這里同樣可以構(gòu)成組合(2, 3, 4),這就構(gòu)成了重復(fù),我們需要去掉這一種方案。去的方法也很簡(jiǎn)單,我們每次移動(dòng)窗口的時(shí)候都記錄下移動(dòng)之前的窗口右邊緣的位置。當(dāng)我們移動(dòng)之后,我們當(dāng)前左側(cè)邊緣和上次右側(cè)邊緣中的特工是重復(fù)的,我們需要去掉相關(guān)情況。

比如說(shuō),我們移動(dòng)之前是[1, 5, 6, 7],移動(dòng)之后是[5, 6, 7, 8, 10, 12]。當(dāng)前的左側(cè)邊緣是5,上次的右側(cè)邊緣是7,[5, 7]這個(gè)區(qū)間內(nèi)的組合數(shù)就是重復(fù)的情況, 我們需要去掉。另外我們還需要注意一下邊界的事情,由于我們計(jì)算組合數(shù)需要用到三方的計(jì)算,而n最大是1e6,三次方之后就是1e18的范圍,顯然使用int是扛不住的,必須要用long long, long long的范圍是2e18,基本上注意這幾點(diǎn)就可以AC了。

最后,我們來(lái)看代碼。

#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <cmath> #include <cstdlib> #include <string> #include <map> #include <set> #include <algorithm> #include "time.h" #include <functional> #define rep(i,a,b) for (int i=a;i<b;i++) #define Rep(i,a,b) for (int i=a;i>=b;i--) #define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++) #define mid ((l+r)>>1) #define lson (k<<1) #define rson (k<<1|1) #define MEM(a,x) memset(a,x,sizeof a) #define L ch[r][0] #define R ch[r][1] #define pii pair<int, int> using namespace std; const int N=1000050; const long long Mod=99997867; int x, y;int arr[N];// 樹(shù)狀數(shù)組 int lowbit(int x) {return x & (-x); }void update(int x, int y) {for (int i = x; i < N; i += lowbit(i)) {arr[i] += y;} }int query(int x) {int ret = 0;for (int i = x; i > 0; i -= lowbit(i)) {ret += arr[i];}return ret; }long long cal(long long x) {return x * (x-1) * (x-2) / (long long) 6; }int main() {int n, d;scanf("%d%d", &n, &d);vector<int> positions;rep(i, 0, n) {int u;scanf("%d", &u);u += 1;positions.push_back(u);update(u, 1);}long long ret = 0;int last = 0;for (int i = 0; i < positions.size(); i++) {int u = positions[i];long long num = query(u+d) - query(u-1);if (num < 3) continue;long long cur = cal(num);// 去掉重復(fù)的可能性if (last > u) {long long cnum = query(last) - query(u-1);cur -= cal(cnum);}ret = ((ret + cur) % Mod + Mod) % Mod;last = u + d;}printf("%lld\n", ret);return 0; }

說(shuō)明

代碼里有一個(gè)細(xì)節(jié),我們?cè)趯?duì)最終結(jié)果進(jìn)行取模的時(shí)候,不是直接% Mod,而是用了一個(gè)比較復(fù)雜的方式:ret = ((ret + cur) % Mod + Mod) % Mod;。這是因?yàn)槲覀冎苯尤∧T谝恍┣闆r下可能會(huì)得到負(fù)數(shù),會(huì)影響我們的結(jié)果,所以需要通過(guò)這種方式來(lái)確保得到的結(jié)果一定是正數(shù)。

總結(jié)

以上是生活随笔為你收集整理的牛客网-数据结构笔试题目(二)-万万没想到之抓捕孔连顺思路解析(附源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。