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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

51 nod 1624 取余最长路 思路:前缀和 + STL(set)二分查找

發布時間:2024/4/19 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 51 nod 1624 取余最长路 思路:前缀和 + STL(set)二分查找 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目:

?

寫這題花了我一上午時間。

?

下面是本人(zhangjiuding)的思考過程:

?

首先想到的是三行,每一行一定要走到。

大概是這樣一張圖

?

每一行長度最少為1。即第一行(i -1) >= 1,第二行 (j - i) >= 1,第三行 (n - j) >= 1。

?

我們要求的就是這條路徑上的和。

我們發現只要 i 和 j 固定了,結果就固定了。

如果每次都要重新求和的話時間復雜度肯定不夠,所以我這個時候想到了前綴和。

用sum[a][b]表示第a行(總共只有3行)前b個數字的和。

?

第一行可以用 sum[1][i]來表示,第二行可以用sum[2][j]-sum[2][i-1]來表示,第三行可以用sum[3][n]-sum[3][j-1]來表示。

(此處用到了i-1所以我們吧sum[][0]空出來,這樣就不會越界)

ans = sum[1][i] + sum[2][j] - sum[2][i-1] + sum[3][n] - sum[3][j-1];

每一組i、j都有一個固定結果,我們求出其中取模最大的那個就好了。

?

?

現在求和的問題解決了,該固定i , j 了。

下面看到i的取值范圍(1 <= i <= n),j的取值范圍(i <= j <= n)

如果遍歷i和j的話時間復雜度還是O(n^2),所以前綴和+暴力肯定是會超時的。

如果能優化一點到O(n*log(n))就好了,這樣就不會超時了。

那么能優化嗎?答案是肯定的。

?

我們先把我們的每一對i,j對應的ans分離一下。

定義up =?sum[1][i] - sum[2][i-1];

  down =?sum[2][j] + sum[3][n] - sum[3][j-1];

這樣up中只含有i,down中只含有j。

因為我們知道結果只和 i , j 有關,

如果我們能把每一個up可能的down存起來,二分法找出那個能讓結果最大的down值,然后用up+down更新ans就好了。

這樣時間復雜度就只有O(n*log(n))了。

?

你可能看到這里還有疑問,先懷著疑問,后面我會把我碰到的問題和疑問都講出來。

?

先看代碼有助于理解:

#include <bits\stdc++.h> using namespace std; typedef long long ll;//定義結構體,value存輸入的值,sum存當前行的前綴和。 struct node{ll value;ll sum; }a[4][100001]; //ifstream in("in15.txt");int main(){int n;ll p;cin >> n >> p;for(int i = 1;i <= 3; i++){for(int j = 1;j <= n; j++){cin >> a[i][j].value;a[i][j].sum = a[i][j].value+a[i][j-1].sum; // 求前綴和。 }}ll ans = 0;set<ll> s; // 用set存當前i值可能碰到的j值對應的down。 set<ll>::iterator it;//反向遍歷是因為down的范圍可以慢慢擴張,不用刪除set中的元素 for(int i = n; i >= 1; i--){ll up = (a[1][i].sum-a[2][i-1].sum)%p; // 當前i值對應的up。 up = (up+p)%p; // 強行變成正數。 ll down = (a[2][i].sum+a[3][n].sum-a[3][i-1].sum)%p; // 當前i值對應的down。 down = (down+p)%p; // 強行變成正數。 // cout << "i:" << i << endl; // cout << "up:"<< up << endl; // cout << "down:" << down << endl; s.insert(down);//因為肯定是j >= i的,所以每次把當前i值對應的down加入到set,set就會表示可能取到的所有的j對應的down的集合。 //明確一點,set里裝的全是down。 // 二分查找,找到一個down值與up相加后最大。 it = s.lower_bound(p-up); // 找到第一個大于等于p-up的down值,我們需要將it--,求出第一個比p-up小的。//注意,1、2順序不能變。 if(it != s.begin()) { //1.如果等于it = s.begin(),因為it還要減一下,相當于沒有找到合適的值。 it--; // 2.it--,讓it指向第一個比p-up小的值。ans = max(ans,(up+*it)%p); // 更新 ans。 }//可能第一個比p-up小的down值加上up后還是比較小,反而down的最大值加上up后取模還比較大,所以話線性時間更新一下ans。 it = s.end();it--;ans = max(ans,(*it+up)%p); }cout << ans << endl;return 0; } //writed by zhangjiuding

?

看到這里可能有一個最大的問題,那就是強行變正數的問題。

比如p = 10, up求出來的是-3,而down求出來是6,(-3 + 6)%10 = 3還是正數,強行將up變成正數是否會出錯呢?

答案是肯定不會, ? up 強行變正后是7,(7+6)% 10 = 3,還是3。

看起來邏輯性并不強,但是我試過了很多組數據都是一樣的結果,強行變正并不影響結果。

?

我的猜測是:因為數組中所有的數都是正數或者0,所以每一個up+down一定會大于0。

      如果up小于0,那么它可以取到的所有的down都會使 up+down >= 0。

      只是這些值是取過模的,所以會出現負數,我們需要將它放在0~(p-1)之間。

?

總結

以上是生活随笔為你收集整理的51 nod 1624 取余最长路 思路:前缀和 + STL(set)二分查找的全部內容,希望文章能夠幫你解決所遇到的問題。

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