<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      信息學競賽中的一些經典思維 (題)

      倍增

      倍增字面上意思是:成倍地增加。當模擬一個過程時,一步一步進行太慢,考慮把模擬的步數二進制分解;經過一些預處理,每次可以模擬 \(2^i\) 步,從而達到優化復雜度的目的。
      倍增主要模型有RMQ,LCA等。

      例題

      給出一個長度為 n 的環和一個常數 k,每次可以從第 i 個點跳到第 (i + k) mod (n+1) 個點,總共跳 m 次。第 i 個點的權值為 a[i],求 m 次跳躍的起點的權值之和 mod 1e9 + 7 。
      數據范圍:$ 1 ≤ n ≤ 10^6 , 1 ≤ m ≤ 10^{18} , 1 ≤ k ≤ n , 0 ≤ a[i] ≤ 10^9 $

      問題分析
      這里顯然不能暴力模擬跳 m 次。因為 最大可到 \(10^{18}\) 級別,如果暴力模擬的話,時間承受不住。
      所以就需要進行一些預處理,提前整合一些信息,以便于在查詢的時候更快得出結果。如果記錄下來每一個可能的跳躍次數的結果的話,不論是時間還是空間都難以承受。
      倍增思想:每個數都可以表示成二進制的形式, 對于從每個點開始的 \(2^i\) 步,記錄一個 go[i][x] 表示第 x 個點跳 \(2^i\) 步之后的終點,而 sum[i][x] 表示第 x 個點跳 \(2^i\) 步之后能獲得的點權和。對于跳 \(2^i\) 步的信息,預處理的時候可以看作是先跳了 \(2^{i?1}\) 步,再跳了 \(2^{i?1}\) 步。
      即有 \(sum[i][x] = sum[i-1][x]+sum[i-1][go[i-1][x]] ,且 go[i][x] = go[i-1][go[i-1][x]]\)
      例如,從1到14的整個跳躍過程由 \(2^3 + 2^2 + 2^0\) 三步組成。也就是說,對于環上這 n 個位置,預處理出每一個位置向前跳 1, 2, 4, ... 次的位置,則必然能夠到達 m。
      實現代碼

      #include <bits/stdc++.h>
      using namespace std;
      
      const int mod = 1000000007;
      
      int modadd(int a, int b) {
        if (a + b >= mod) return a + b - mod;  // 減法代替取模,加快運算
        return a + b;
      }
      
      int vi[1000005];
      
      int go[75][1000005];  // 將數組稍微開大以避免越界,小的一維盡量定義在前面
      int sum[75][1000005];
      
      int main() {
        int n, k;
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; ++i) {
          scanf("%d", vi + i);
        }
      
        for (int i = 1; i <= n; ++i) {
          go[0][i] = (i + k) % n + 1;
          sum[0][i] = vi[i];
        }
      
      //int logn = 31 - __builtin_clz(n);  // 一個快捷的取對數的方法
        int logn = 65;
        for (int i = 1; i <= logn; ++i) {
          for (int j = 1; j <= n; ++j) {
            go[i][j] = go[i - 1][go[i - 1][j]];
            sum[i][j] = modadd(sum[i - 1][j], sum[i - 1][go[i - 1][j]]);
          }
        }
      
        long long m;
        scanf("%lld", &m);
      
        int ans = 0;
        int curx = 1;
        for (int i = 0; m; ++i) {
          if (m & (1 << i)) {  // 參見位運算的相關內容,意為 m 的第 i 位是否為 1
            ans = modadd(ans, sum[i][curx]);
            curx = go[i][curx];
            m ^= 1ll << i;  // 將第 i 位置零
          }
        }
      
        printf("%d\n", ans);
      }
      

      這題的 \(m≤10^{18}\) ,雖然看似恐怖,但是實際上只需要預處理出 65 以內的 i ,就可以輕松解決,比起暴力枚舉快了很多。用行話講,這個做法的時間復雜度是預處理 O(nlogm) ,查詢每次 O(logm) 。
      倍增除了作為一種獨立的思想以外,還經常被應用到各種算法里面,例如 快速冪、LCA 和 RMQ 問題 。

      總結: 這就是倍增預處理出以二的整數次冪為單位的信息:
      *在遞推中,如果狀態空間很大,可以通過成倍增長的方式,只遞推出狀態空間在2的整數次冪的值作為代表。
      *每個數都可以表示成二進制的形式,可用之前的求出的代表值拼成所需的值。
      *要求這個遞推問題的狀態空間關于2的次冪具有可劃分性。
      注意:為了保證統計的時候不重不漏,一般預處理出左閉右開的點權和。

      Luogu P1419 尋找段落

      題目描述

      給定一個長度為n的序列ai,定義a[i]為第i個元素的價值。現在需要找出序列中最有價值的“段落”。段落的定義是長度在[S,T]之間的連續序列。最有價值段落是指平均值最大的段落,段落的平均值=段落總價值/段落長度。

      輸入輸出格式

      輸入格式:

      第一行一個整數n,表示序列長度。
      第二行兩個整數S和T,表示段落長度的范圍,在[S,T]之間。
      第三行到第n+2行,每行一個整數表示每個元素的價值指數。

      輸出格式:

      一個實數,保留3位小數,表示最優段落的平均值。

      輸入輸出樣例

      輸入樣例#1:

      3
      2 2
      3
      -1
      2

      輸出樣例#1:

      1.000

      數據范圍

      對于30%的數據有n<=1000。
      對于100%的數據有n<=100000,1<=S<=T<=n,-10000<=元素價值<=10000。

      解題思路

      可以看出所求問題的答案具有單調性,考慮二分答案,變為判定性問題。
      對于可能的答案k,設b[i]=a[i]-k。
      就是求b數組一段長度在s,t之間的和的最大值,判斷其是否大于等于0。求區間和的最大值可以用前綴和+單調隊列維護。
      具體過程,貼一份洛谷題解的圖:

      時間復雜度

      假設數據范圍為A,則二分答案是O(logA)的,判斷一次用了前綴和和單調隊列,復雜度是O(n)的,總時間復雜度為O(nlogA)。
      實現代碼

      #include <bits/stdc++.h>
      using namespace std;
      int n,S,T,a[100005],q[100005];//q數組用來記錄前綴和的下標
      double Sum[100005];           //前綴和記錄到第i個的減去平均值的和
      int check(double m)
      {
          Sum[0]=0;
          for(int i=1;i<=n;i++)
              Sum[i]=Sum[i-1]+a[i]-m; //初始化差值的前綴和 
       
          int head=1,tail=0;	        //head為單調(上升)隊列的左端點,tail為右端點。
          for(int i=1;i<=n;i++)
          {
              if(i>=S)
              {
                  //一直減小單調隊列的右端點,隊列右端點處的位置其前綴和小于Sum[i-S]
                  //單調隊列 q[] 中存的是,假設區間的右端點是i,區間前面的左端點最小可行位置 i-S 
                  while(head<=tail&&Sum[i-S]<Sum[q[tail]])
                      tail--;     //維護隊列的右端點
                  q[++tail]=i-S;  //對于每一個右邊的i,都將它的區間左端點初始化為 i-S;
              }
              //單調隊列的隊首存儲的區間左端點位置,小于滿足區間長度[S,T]范圍時,i位置的左端點最小值;
              if(head<=tail&&i-q[head]>T)	
                  head++;
              //如果這一段區間內的和大于等于0,則證明有這個值可行,且可能由更大的平均值存在。
              if(head<=tail&&Sum[i]-Sum[q[head]]>=0)
                  return 1;
          }
          return 0;
      }
      int main()
      {
          cin>>n>>S>>T;
          for(int i=1;i<=n;++i)
          {
              cin>>a[i];
          }
          double l=-10005,m,r=10005;
          while(l+1e-5<r)  // 二分區間平均值的最大值,注意控制精度保留三位小數 
          {
              m=(l+r)/2;
              if(check(m))
                  l=m;
              else
                  r=m;
          }
          cout <<fixed<<setprecision(3)<<l<< endl;
          return 0;
      }
      
      posted @ 2020-07-10 16:17  _tham  閱讀(1125)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 国产99青青成人A在线| 真实国产乱啪福利露脸| 株洲市| 人妻少妇精品无码专区| 国产综合久久99久久| 亚洲男人av香蕉爽爽爽爽| 久久国产成人高清精品亚洲| 国产精品播放一区二区三区| 在线免费观看毛片av| 国产一区二区三区精品综合 | 激情国产一区二区三区四区| 999福利激情视频| 亚洲鸥美日韩精品久久| 日本一区二区三区在线看| 黑人巨茎大战白人美女| 日韩av裸体在线播放| 国产精品国产精品国产精品| 起碰免费公开97在线视频| 亚洲aⅴ无码专区在线观看q| 日韩av日韩av在线| 免费人成视频在线观看不卡| 中文字幕av一区| 无码专区 人妻系列 在线| 中文字幕国产精品综合| 亚洲成av人片天堂网无码| 一区二区和激情视频| 亚洲熟妇精品一区二区| 精品亚洲欧美高清不卡高清| 日本久久精品一区二区三区| 亚洲国产精品无码一区二区三区| 精品国产一区二区三区av色诱| 五月天免费中文字幕av| 国产日韩综合av在线| 亚洲成在人天堂一区二区| 97久久精品人人做人人爽| 欧美大胆老熟妇乱子伦视频| 日韩av片无码一区二区不卡| 精品久久久久久无码人妻蜜桃| 亚洲成av人片不卡无码手机版| 香港日本三级亚洲三级| 日本不卡一区|