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\)
其中 \(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;
}