牛客IOI周賽20-普及組
阿新 • • 發佈:2020-11-30
牛客IOI周賽20-普及組
完全數
牛客的簽到題,最暴力的做法就是把數每個因子羅列出來,但是這樣只有60的暴力分,我們從題目的資料可以看到
資料範圍是1e7的,在學習素數的時候我們知道一個因子就能推出另一個因子,所以我們沒必要從1判斷到n-1
直接i*i <= n就能找到所有的因子,但是注意像16這樣的數,當我們的i為4的時候,這個4的因子只能算一次
Code:
#include<bits/stdc++.h> using namespace std; int main() { long long n; scanf("%lld",&n); long long sum = 1; for(long long i = 2; i * i <= n; ++i) { if(n % i == 0) sum += n/i == i? 0 : n/i + i ;//判斷是否是重因子 } if(sum > n) { puts("Late"); } else if(sum == n) { puts("Pure"); } else if(sum < n) { puts("Early"); } return 0; }
移動撤銷
解題思路:這個題是一個模擬題,我們先記錄牛牛的操作,然後在記錄的時候每次遇到Z,就把上一個操作刪掉
最後直接按照沒有Z的操作模擬,然後輸出最終的座標,注意這裡的Z,可能Z的上一個位置沒有任何操作,用棧的同學請注意空棧的情況
Code:
#include<bits/stdc++.h> using namespace std; int main() { int n; vector<char> V; char c; cin >> n; for(int i = 0; i < n; ++i) { cin>>c; if(c == 'Z') { if(V.size())//當V不為空的時候才進行撤銷操作 V.pop_back(); } else { V.push_back(c); } } int x,y; x = y = 0; for(int i = 0; i < V.size(); ++i) {//直接模擬 if(V[i] == 'W') { y++; } else if(V[i] == 'A') { x--; } else if(V[i] == 'S') { y--; } else if(V[i] == 'D') { x++; } } cout << x << " " << y << endl;//最後輸出x和y return 0; }
石頭剪刀布
解題思路:牛牛要想分數拿的高,那麼就要儘可能的不輸,我們可以計算牛牛最多能贏的場數,然後再把贏的場數從遊戲場數剪掉
然後再計算平局可能的場數就是最高的分數,然後……然後就AC了呀
Code:
#include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; int a1,b1,c1; int a2,b2,c2; cin>>a1>>b1>>c1; cin>>a2>>b2>>c2; int win1,win2,win3;//這是牛牛可能會贏的場數(分別對應著石頭、剪刀、布) long long sum = 0; win1 = min(a1,b2); win2 = min(b1,c2); win3 = min(c1,a2); sum += win1 * 2; sum += win2 * 2; sum += win3 * 2; a1 -= win1;//這裡分別減去牛牛贏的場數 b2 -= win1; b1 -= win2; c2 -= win2; c1 -= win3; a2 -= win3; sum += min(a1,a2) + min(b1,b2) + min(c1,c2);//最後再把可能平局的場數加起來就好啦 cout<<sum<<endl; return 0; }
夾縫中求和
解題思路:我們要求範圍在X和Y之間的不同數的兩兩組合,且i<j,很明顯數的位置不重要,我們就可以隊陣列進行排序操作
然後我們列舉i,找到滿足條件的aj的範圍,aj的範圍是X到Y的,所以我們可以用二分查詢找到j的範圍(因為前面排過序了)
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int a[N];
int find(int l,int r,int key)
{
while(l+1<r)
{
int mid=l+r>>1;
if(a[mid]<=key)
l=mid;
else
r=mid;
}
return r;
}
int main()
{
int n,x,y;
scanf("%d%d%d",&n,&x,&y);
for(int i = 0;i < n; ++i) {
scanf("%d",&a[i]);
}
long long sum = 0;
sort(a,a+n);
for(int i = 0;i < n; ++i) {
int k1 = x - a[i];
int k2 = y - a[i];
int loc1 = max(i+1,find(-1,n,k1 - 1));//找到滿足條件的左邊的位置
int loc2 = max(loc1,find(-1,n,k2));//右邊的位置
sum += max(0,loc2 - loc1);
}
printf("%lld\n",sum);
return 0;
}
當然這題還能用雙指標做,但是不知道為什麼我的雙指標寫出來炸了,然後寫了個'單指標'?hh
如果但看這個左邊界或者有邊界的話,我們可以直接寫出一個0(N)的操作,然後我們會發現這樣寫的話會有值的重疊
所以我們直接把考慮右邊界的情況減去左邊界的情況就OK啦,不懂的話可以手算一下哦
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100005;
int a[N];
int main()
{
int n,x,y;
scanf("%d%d%d",&n,&x,&y);
for(int i = 0;i < n; ++i) {
scanf("%d",&a[i]);
}
sort(a,a+n);
int l = 0, r = 0;
long long sum = 0;
for(int i = 0, j = n - 1 ;i < n; ++i) {//計算的是隻考慮有邊界的情況
while(j > 0 && a[i] + a[j] > y) j--;
if(j < i) break;
sum += j - i;
}
for(int i = 0, j = n - 1 ;i < n; ++i) {//計算的是隻考慮左邊界的情況
while(j > 0 && a[i] + a[j] >= x) j--;
if(j < i) break;
sum -= j - i;
}
printf("%lld\n",sum);
}
第一次AK,++rp;