1. 程式人生 > 實用技巧 >牛客IOI周賽20-普及組

牛客IOI周賽20-普及組

牛客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;