1. 程式人生 > >【11.2晚校內測試】【裝桶模擬】【單調棧】

【11.2晚校內測試】【裝桶模擬】【單調棧】

真的是fo了,晚上還來一次測試......


mister
【問題描述】 不久前 Mister 從太空中探測到一個奇怪的訊號,他開始研究這個訊號。 經過一些變換後,這個訊號變成了長度為 n 的排列或者它的迴圈移位。對於進一步的研究 Mister 需要一些資料分析,這就是為什麼他決定選擇這個排列的迴圈移位,它有最小的可 能偏差。
我們把排列的偏差定義為

求一個可能偏差最小的排列 p 的迴圈移位。 讓我們表示 k(0≤k < n)的迴圈移位排列 p 的變化需要達到這種轉變,例如: k = 0: 變成 p1 p2 … pn, k = 1: 變成 pn p1 … pn - 1, …, k = n - 1: 變成 p2 p3 … pn p1。


【輸入格式】 第一行包含單個整數 n(2≤n≤10^6)——排列的長度。 第二行包含 n 個空格分隔的整數 p1 p2 … pn(1≤pi≤n)的元素排列。 保證所有元素都是不同的。
【輸出格式】 輸出一個整數:排列 p 的迴圈移位的最小偏差。
【樣例資料】 Input 3 2 3 1 Output 0 Input 3 3 2 1 Output 2
【資料範圍】 10%的資料:n<=100 30%的資料:n<=2000 70%的資料:n<=50000 100%的資料:n<=1000000


 Solution

 一開始毫無頭緒QAQ

發現規律,如果當前位置的值大於這個位置,那麼每往後平移一步,答案會減少1,反之答案會增加1。以下把$p[i]-i$稱為某一位的貢獻,$p[i]-i>=0$稱為貢獻為正,反之稱為貢獻為負。

所以考慮怎麼每次快速維護答案的增減量。如上訴,明顯需要維護每平移一步貢獻從正變成負和從負變成正的數的個數,開個桶可以$O(1)$處理出初始每個數需要多少步會改變符號,裝到桶裡表示經過$i$步有多少個數貢獻會變負。

發現如果當前位置貢獻為負,那麼往後平移貢獻會始終為負,除了最後一位跳到第一位可能會變化,進行特殊處理即可。

所以每次貢獻為正的數量減去之前桶處理的,貢獻為負的加上桶處理的,就是當前貢獻為正和為負的數量。答案減去正的加上負的再更新最後一位的特判就是新的答案。

還有一些細節在程式碼中展示。

Code

#include<iostream>
#include
<algorithm> #include<cmath> #include<cstdio> #define LL long long using namespace std; int p[2000005], cur[2000005]; int main() { int n; scanf("%d", &n); LL sum = 0, L = 0, R = 0, tmp = 0; for(int i = 1; i <= n; i ++) { scanf("%d", &p[i]); sum += abs(p[i] - i); if(p[i] >= i) L ++, cur[p[i] - i] ++; else R ++; } LL ans = sum; for(int i = 0; i < n - 1; i ++) {////i表示的是進行i+1次平移(方便下標寫法 L -= cur[i]; R += cur[i]; sum = sum - L + R - 1 - abs(p[n - i] - n) + p[n - i] - 1;//第n位貢獻一定為負,所以要減去負的貢獻中多算的一個 cur[p[n - i] + i] ++;//第一位貢獻一定為正 L ++, R --; if(sum < ans) ans = sum, tmp = i + 1; } printf("%lld %d\n", ans, tmp); return 0; }

array
【問題描述】 給你一個由 n 個元素組成的陣列。這個陣列的某個連續子序列的不平衡值是這個段中最大 和最小元素的差值。陣列的不平衡值是該陣列所有連續子序列不平衡值的總和。 例如,陣列[1,4,1]的失衡值為 9,因為這個陣列有 6 個不同的子段: [1](從 a1 到 a1),失衡值為 0; [1,4](從 a1 到 a2),失衡值為 3; [1,4,1](從 a1 到 a3),失衡值為 3; [4](從 a2 到 a2),失衡值為 0; [4,1](從 a2 到 a3),失衡值為 3; [1](從 a3 到 a3),失衡值為 0; 你需要確定陣列 a 的不平衡值。
【輸入格式】 第一行包含一個整數 n(1≤n≤10^6)陣列的長度。 第二行包含 n 個整數 a1 a2 … an (1≤ai≤10^6)——的元素陣列。
【輸出格式】 輸出一個整數: a 陣列的不平衡值。
【樣例資料】 Input 3 1 4 1 Output 9
【資料範圍】 10%的資料:n<=100 30%的資料:n<=5000 70%的資料:n<=10^5 100%的資料:n<=10^6


Solution

看起來好單調棧!!但是為什麼仔細一想好不可做??

實際上就是單調棧....

答案就等於$\sum{a[i]*(以a[i]為最大值的區間個數-以a[i]為最小值的區間個數}$QAQ,仔細想想是不是好有道理QAQAQAQ

Code

#include<bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, t = 0; char ch = getchar();
    while(!isdigit(ch))    t |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x *= (t ? -1 : 1);
}

int l[1000005], r[1000005], a[1000005], stk[1000005], top, n;
long long ans;
int main() {
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)    a[i] = read();
    for(int i = 1; i <= n; i ++)    l[i] = r[i] = i;
    for(int i = 1; i <= n; i ++) {
        while(top && a[i] >= a[stk[top]]) {
            r[stk[top]] = i - 1;
            top --;
        }
        l[i] = stk[top] + 1;
        stk[++top] = i;
    }
    while(top) {
        r[stk[top]] = n;
        top --;
    }
    for(int i = 1; i <= n; i ++)    ans += 1ll * a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    for(int i = 1; i <= n; i ++)    l[i] = r[i] = i;    top = 0;
    for(int i = 1; i <= n; i ++) {
        while(top && a[stk[top]] >= a[i]) {
            r[stk[top]] = i - 1;
            top --;
        }
        l[i] = stk[top] + 1;
        stk[++top] = i;
    }
    while(top) {
        r[stk[top]] = n;
        top --;
    }
    for(int i = 1; i <= n; i ++)    ans -= 1ll * a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    printf("%lld", ans);
    return 0;
}