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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

題意

我叫王大錘,是一名特工。我剛剛接到任務:在字節跳動大街進行埋伏,抓捕恐怖分子孔連順。和我一起行動的還有另外兩名特工,我提議

我們在字節跳動大街的N個建筑中選定3個埋伏地點。

為了相互照應,我們決定相距最遠的兩名特工間的距離不超過D。

我特喵是個天才! 經過精密的計算,我們從X種可行的埋伏方案中選擇了一種。這個方案萬無一失,顫抖吧,孔連順!

……

萬萬沒想到,計劃還是失敗了,孔連順化妝成小龍女,混在cosplay的隊伍中逃出了字節跳動大街。只怪他的偽裝太成功了,就是楊過本人來了也發現不了的!

請聽題:給定N(可選作為埋伏點的建筑物數)、D(相距最遠的兩名特工間的距離的最大值)以及可選建筑的坐標,計算在這次行動中,大錘的小隊有多少種埋伏選擇。

注意:

兩個特工不能埋伏在同一地點

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

輸入描述:

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

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

題解

首先我們來理一下題目當中出現的幾個點,我們要算的是特工埋伏的方法數。特工埋伏需要固定3個特工,并且要求特工之間的距離小于D。

題目當中還說特工之間是等價的,如果大家敏感的話,這其實是一道排列組合問題,準確的說是一道組合問題。這和n球里任意取3個出來有多少種組合本質上是一樣的,如果大家還記得組合公式的話套一下就可以了:

這里的k固定了是3,我們代入進去可以簡化為:
,所以現在唯一的問題就是n不知道。我們可以知道n是可以通過D來進行計算的,至于怎么計算我們先放一放,先來對問題進行一個建模。

首先,由于題目當中說的是特工分布在一條大街上,我們可以把大街看成是橫坐標軸,每一個特工就是坐標軸上的一個點。由于題目當中限定了3個特工之間的距離不超過D,其實也就是最左側和最右側的距離小于D。我們可以想象有一個長度為D的線段在這個坐標軸上滑動,窗口內的特工數量就是n。

由于特工的位置是固定的,所以這個n是很好算的。假如我們用一個數組sum來存儲從0開始到當前位置一共包含的特工數,那么區間[x, x+D]范圍內的特工數量其實就等于sum[x+D]-sum[x]。這里我們可以通過差分數組來搞定,也可以使用樹狀數組,我這里用了樹狀數組,其實沒有必要,因為不涉及更新操作。

所以接下來我們要做的就是把這個窗口從最左側往右滑動就可以了,每次窗口內特工變化的時候,我們計算一下構成的組合數累加在一起就可以了。進一步我們可以想到,其實我們只需要關心窗口的左邊緣,我們把窗口的左邊緣和特工重疊,這樣能夠使得窗口內的特工數量盡可能多。

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

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

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

最后,我們來看代碼。

#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];// 樹狀數組 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);// 去掉重復的可能性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; }

說明

代碼里有一個細節,我們在對最終結果進行取模的時候,不是直接% Mod,而是用了一個比較復雜的方式:ret = ((ret + cur) % Mod + Mod) % Mod;。這是因為我們直接取模在一些情況下可能會得到負數,會影響我們的結果,所以需要通過這種方式來確保得到的結果一定是正數。

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。