1. 程式人生 > 實用技巧 >10.22 考試總結

10.22 考試總結

教練說之前的題太簡單了,所以直接把省選的模擬賽搬過來了,???

wdnmd, 那豈不是要爆零了?

T1 氣泡排序

氣泡排序是一種簡單的排序,程式碼如下。

counter = 0
while A 不是有序的
      counter = counter + 1
      for i = 1 to (n-1)
      	  IF A[i] > A[i+1]
      	     Then Swap(A[i],A[i+1])
      	  End If
       End for
End while       

現在給定一個排列 \(A\) ,請你求出氣泡排序幾輪後 \(A\)

會有序,即 變數 \(counter\) 的值

其中 \(A\) 陣列用一下方法生成。

for(int i = 1; i <= n; i++)
{
	a[i] = i;
	s = (s * b + c) % d;
	swap(a[i],a[(s%i)+1]);
}

對於 30% 的資料 \(n\leq 10^3\)

對於 70% 的資料 \(n\leq 10^6\)

對於 100% 的資料 \(n\leq 3\times 10^7\)

輸入僅一行包括五個整數 \(n,s,b,c,d\)\(n\) 表示排列長度。


sloution

暴力分還是很好拿的。

30pts 直接 \(O(n^2)\)

列舉就行。

for(int i = 1; i <= n; i++)
{
    a[i] = i;
    s = (s * B + C) % D;
    swap(a[i],a[(s%i)+1]);
}
    
ans = 0;
while(1)
{
    ans++;
    int num = 0;
    for(int i = 1; i <= n-1; i++)
    {
    	if(a[i] > a[i+1]) swap(a[i],a[i+1]), num++;
	}
	if(num == 0) break;
}
printf("%d\n",ans-1);

70pts

通過分析一波發現我們要求的答案其實是 以 \(i\) 為結尾的滿足 \(j < i 且 a_j > a_i\) 的數對的數量最大值, 也就是在 \(i\) 之前比 \(a_i\) 大的數量。

然後我們可以優化一下暴力,用樹狀陣列可以優化到 \(nlogn\)

然後 \(70pts\) 就到手了,但我考試的時候沒開 long long ,然後就由 70pts--- 30pts

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 3e7+10;
int n,s,B,C,D,ans;
int a[N],b[N],tr[N];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
void YYCH()
{
    for(int i = 1; i <= n; i++)
    {
        a[i] = i;
        s = (s * B + C) % D;
        swap(a[i],a[(s%i)+1]);
    }
}
int lowbit(int x){ return x & -x;}
void chenge(int x,int val)
{
    for(; x <= N-5; x += lowbit(x)) tr[x] += val;
}
int query(int x)
{
    int res = 0;
    for(; x; x -= lowbit(x)) res += tr[x];
    return res;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
    n = read(); s = read(); B = read(); C = read(); D = read(); YYCH();
    for(int i = 1; i <= n; i++) b[i] = a[i];
    sort(b+1,b+n+1);
    int num = unique(b+1,b+n+1)-b-1;
    for(int i = 1; i <= n; i++)
    {
        a[i] = lower_bound(b+1,b+num+1,a[i])-b;
    }
    for(int i = 1; i <= n; i++)
    {
        chenge(a[i],1);
        ans = max(ans, i - query(a[i]));
    }
    printf("%d\n",ans);
    fclose(stdin); fclose(stdout);
    return 0;
}

滿分做法

考試的時候想到了答案是每個數前面比他大的數的數量的最大值,因為前面比他小的數對答案沒有影響。

然後就寫了種差分的寫法,但不知道哪裡寫鍋了,對拍一直出錯。

考完看了題解才發現原來不用差分。

我們會發現這道題需要一種 \(O(n)\) 的做法,但我們上個演算法的瓶頸就在於樹狀陣列的 \(log\) 無法抹去。

但我們可以發現這 \(A\) 陣列的生成方式有貓膩。每次交換的都是比他小的數。

假設當前 \(a[tmp]\) 要和 \(a[i]\) 換,那麼對答案的貢獻就是 \(a_i - a_{tmp}\)

因為前面是個 \(1-i\) 的全排列,我們把 \(a_{tmp}\) 換到 \(i\) 這個位置,比他大的數只會有 \(a_i - a_{tmp}\) 個。

所以我們最後只需要輸出 \(max(a_i - a_{tmp})\) 就可以。

複雜度 \(O(n)\)

正確性,對拍請。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e7+10;
int n,s,B,C,D,ans;
int a[N],b[N],tr[N];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
void YYCH()
{
    for(int i = 1; i <= n; i++)
    {
        a[i] = i;
        s = (1LL * s * B + 1LL * C) % D;
        int tmp = (s % i) + 1;
        ans = max(ans,a[i]-a[tmp]);
        swap(a[i],a[tmp]);
    }
}
int main()
{
	freopen("bubble.in","r",stdin);
	freopen("bubble.out","w",stdout);
    n = read(); s = read(); B = read(); C = read(); D = read(); YYCH();
    printf("%d\n",ans);
    fclose(stdin); fclose(stdout);
    return 0;
}