1. 程式人生 > 其它 >AtCoder Beginner Contest 212題解

AtCoder Beginner Contest 212題解

前言

好久沒有打ABC了 ,上一次打已經是ABC196了,震驚這次有8題,世界第一也來了,30min就全切了,可惜我只會ABCD。

upd:我好像會E了。

Alloy

題目大意

給兩個數,要判斷這兩個數在哪個範圍,範圍就不用解釋了吧······

題解

直接if判斷即可,非常簡單。

程式碼

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a,b;
	cin>>a>>b;
	if(a==0&&b>0)puts("Silver");
	if(a>0&&b==0)puts("Gold");
	if(a>0&&b>0)puts("Alloy");
	return 0;
} 

Weak Password

題目大意

給一個數字的 PIN,要判斷這個 PIN 的強弱,如果出現下面兩種情況之一,就算弱:

  • 四位相同,見樣例一。
  • 每相鄰兩位遞增\(1\),如\(0\)後接著\(1\)\(1\)後接著\(2\),注意\(9\)後接著\(0\),見樣例三。

題解

對於判定\(1\),直接if判斷即。

對於判定\(2\),掃一遍數列合不合法,因為\(9\)後接著\(0\),所以要特別判定一下,當然也可以直接與\(10\)取餘。

輸入時用字串處理比較方便,注意陣列下標從\(0\)開始。

程式碼

#include<bits/stdc++.h>
using namespace std;
int main()
{
	char ch[5];
	bool flag;
	cin>>ch;
	if(ch[0]==ch[1]&&ch[1]==ch[2]&&ch[2]==ch[3])
	{
		puts("Weak");
		return 0;
	}
	flag=true;
	for(int i=0;i<=2;i++)
		if(ch[i+1]-'0'!=(ch[i]-'0'+1)%10)
		{
			flag=false;
			break;
		}	
	if(flag==true)puts("Weak");
	else puts("Strong");
	return 0;
} 

Min Difference

題目大意

已知兩個數列\(A\)\(B\),求:

\[\min|A_i-B_j|(1\le i\le n,1\le j\le m) \]

題解

很明顯有\(O(nm)\)的暴力,可過不了這道題,考慮優化。

對於答案,我們只尋找差的絕對值較小的,而不用關心差的絕對值一定大的數,因此對於每個數而言,其實只用計算比自己大和自己小的離自己最近的兩個數即可。

所以我們先將兩個數列排序,再解決上面的問題。

但如何解決使兩個數差距儘可能小呢?可以使用雙指標\(i\)\(j\)表示\(A_i\)\(B_j\),我們要先思考:如果知道\(A_i\)\(B_j\)的大小關係,是增加\(i\)

還是增加\(j\)更對答案有貢獻。

這要分成兩種情況(注意已經按升序排好):

  • \(A_i>B_j\),這時候增加\(j\),因為增加\(i\)只會使兩個數差距越來越大。
  • \(A_i\le B_j\),這時候增加\(i\),因為增加\(j\)只會使兩個數差距越來越大。

注意兩個指標會遍歷兩個數列,所以遍歷的時間複雜度為\(O(n+m)\),加上排序,總時間為\(O(n\log n+m\log m)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int a[N],b[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)scanf("%d",&b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    int ans=INT_MAX;
    for(int i=1,j=1;i<=n&&j<=m;)
    {
        ans=min(ans,abs(a[i]-b[j]));
        if(a[i]>b[j]) j++;
        else i++;
    }
    printf("%d",ans);
    return 0;
}

Querying Multiset

題目大意

維護一個包?資料結構,滿足以下三種操作:

  • 在一個空球上面寫一個整數\(X\),並把這個球放入包內。
  • 對於包內的所有球,將每個球上面的整數加上\(X\)
  • 輸出包中所有球上的最小的數字,並把這個球扔掉.

題解

對於這種帶加入刪除的整體最大最小值問題,很明顯就是堆,對於這道題,只要維護一個小根堆。

因為操作\(2\)不可能將所有數都暴力模擬,但對於同一基準的數,不管加上多少,數的大小關係總不變,因此我們來解決如何把數都變成同一標準。

先記錄一下操作\(2\)加了多少,記為\(sum\),這樣每個數就可以表示為\(a_i+sum\)\(a_i\)為同一基準數)。

先不考慮操作\(3\),對於每次操作\(1\),將\(X-sum\)就可以將加入的數\(X\)與堆中的其他數變成同一標準,因為這個數加入之前的操作\(2\)的值與這個數是無關的,如果還不明白,那就舉個例子:

假設現在只有一個數\(X\),進行了一次操作\(2\),所加總和記為\(sum\)

進行了一次操作\(1\),加入了一個數\(Y-sum\)

又進行了一次操作\(2\),加上了一個數\(k\),所加總和記為\(sum+k\)

此時\(X\)的值為\(X+sum+k\)\(Y\)的值為\(Y-sum+sum+k=Y+k\)

操作\(2\)很簡單,直接將\(sum+X\)就可以了。

操作\(3\)的話也很簡單,直接取隊頭的值\(a_{top}+sum\)就可以了。

注意開long long,時間複雜度為\(O(n\log n)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
priority_queue<LL,vector<LL>,greater<LL> > heap;
LL sum;
int main()
{
    scanf("%d",&n);
    while(n--){
        int op,x;
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&x);
            heap.push(x-sum);
        }
        else if(op==2){
            scanf("%d",&x);
            sum+=x;
        }
        else{
            printf("%lld\n",heap.top()+sum);
            heap.pop();
        }
    }
    return 0;
}