CometOJ歡樂賽總結

今天下午嘗試了下CometOJ的歡樂賽,好吧,是對那些大佬來說是歡樂賽,其中出了9道題,5個小時,我只A了2道,其中有三道都是超時,所以最後也沒做出了,關於題目,比賽完只搞定了其中5題,覺得是自己比賽應該可以搞定的。

B 距離產生美

在這裏插入圖片描述
這道簽到題我也花了半個小時(黑臉),這道其實關鍵在於選擇合適的貪心算法,就是每次更新變量的時候需要判斷它前後兩個變量的值,我們的貪心思想是:儘量選擇更新中間那個變量,這樣可以使得所需要更新得變量儘可能地少,即是題目要求

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const long long inf = 1e18;
inline int read(){
    char ch = getchar(); int x = 0, f = 1;
    while(ch< '0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main(){
	int n=read(),k=read();
	long long a[maxn];
	int ans = 0;
	for(int i=0;i<n;i++)
		a[i] = read();
	for(int i=1;i<n;i++){
		if(fabs(a[i]-a[i-1]) < k){
			if(fabs(a[i+1]-a[i])<k){
				a[i] = inf;
				ans++;
			}else{
				a[i-1] = inf;
				ans++;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

C 烤麪包片

在這裏插入圖片描述
這道題原來我以爲卡的是時間和數據量,對於階乘來說1e9的數據是真的太大了,這樣的一般複雜度是O(1),但比賽的時候我沒有反應過來,超時了一直以爲是算法不夠優化。嘗試各種算法,包括二分求階乘,還有通過二進制來計算大數據量,像下面這個,兩數相乘,通過將其中一個數化爲二進制,利用按位與來對數相乘不斷取模,從而不會爆數據。
在這裏插入圖片描述
其中還想到用二分遞歸,但數據量真的太大啦,這點優化完全不濟於事。所以輸入大的數據的會發現出現程序動不了了。
在這裏插入圖片描述
這題的真正解決思路是:首先根據範圍可得 4!!一定大於109 (事實上甚至大於1018),所
以 4!!!一定能被 mod 整除(n>4 也同理)。那麼我們只需要判斷 n≤3
的情況了。直接計算即可。如果寫一個階乘函數,然後調用連續三次
來獲得答案,如:((n!%mod)!%mod)!%mod 可能會無法通過以下的 text:
2 2
(本應輸出 0,但三次調用階乘函數最終會輸出 1,因爲第一次調用
得到 0,之後再調用階乘函數發現 0!是 1)

一個較好的處理方式是將 n=0,1,2 特判掉,然後預先計算出 3!!爲
720,然後用一次 for 循環解決這道題。

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long n,mod,ans=1;
	cin >> n >> mod;
	if(n==0)
		return cout << 1%mod,0;
	if(n<3)
		return cout << n%mod,0;
	if(n>3)
		return cout << 0,0;
	for(int i=1;i<=720;i++)
		ans = (ans*i)%mod;
	return cout << ans,0;
}

G 籃球校賽

在這裏插入圖片描述
這道題比賽的時候沒有用任何特殊的操作或者是剪枝就直接遞歸,雖然能算出答案,但導致程序計算時計算了很多沒有用的量,耗費了大量的時間,然後我就卡在時間上,比賽過後,看着大佬的題解:
大佬是在存入數據的時候就按能力值從1-5排序更新每一個位置,並把這些數據存入到一個二維數組中,在調用dfs遞歸的時候,就根據這個二維數組進行遞歸和計算,節省了很多時間,但有些人說可以用五次循環直接暴力得出,這個我還沒有嘗試。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+2;
const int N = 5;
ll a[maxn][N+1],ans=0;
bool vis[maxn];//記錄是否取某個球員
ll mmax[N+1][N+1];
void dfs(int cur,ll temp){
	if(cur == N+1){
		ans = max(ans,temp); 
		return;
	}
	for(int i=1;i<=N;i++){
		if(vis[mmax[i][cur]])
			continue;
		vis[mmax[i][cur]] = true;
		dfs(cur+1,temp+a[mmax[i][cur]][cur]);
		vis[mmax[i][cur]] = false;
	}
}
int main(){
	std::ios::sync_with_stdio(false); 
	int n;
	cin >> n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=N;j++){
			cin >> a[i][j];
			for(int pos=1;pos<=N;pos++){ // pos在該能力的排位 
				if(a[i][j] > a[mmax[pos][j]][j]){
					for(int k=N;k>=pos+1;k--)
						mmax[k][j] = mmax[k-1][j];//將這些元素後移一位
					mmax[pos][j] = i;//插入
					break;
				}
			}
		}
	}
	dfs(1,0);
	cout << ans << endl;
	return 0;
}

G 三元組

在這裏插入圖片描述
出題人思路:對於一組滿足條件的(i,j):
2 ∗ min(?? + ??,?? + ??) ≤ max(?? + ??,?? + ??)
也必然滿足:
min(ai + aj,?? + ??) ≤ max(?? + ??,?? + ??)
不妨設 2(?? + ??) ≤ ?? + ??,即ai + aj ≤ ?? + ??
此時,顯然不需要考慮min(?? + ??,?? + ??) = ?? + ??這個條件
移項可得 (2 ∗ ?? − bi) + (2 ∗ ?? − bj) ≤ 0
直接按照2 ∗ ?? − ??升序進行排序,記數組爲P
此時可以發現性質:
若??最多隻能和? ?匹配,則?? + 1只能和[i + 1,j]之間的匹配
此時使用滑窗即可計算答案
又因爲我們只考慮了2(?? + ??) ≤ bi + bj的情況
再對每個三元組中的??,bi進行交換,計算一次答案即可。
時間複雜度O(n + nlog2 ?)

//三元組 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n;
ll sum[maxn];
struct node{
	ll a,b,c;
}p[maxn];

bool cmp(node x,node y){
	return (2*x.a-x.b) < (2*y.a-y.b);
}//比較函數 

ll solve(){
	sort(p+1,p+n+1,cmp);
	sum[0] = 0;
	for(int i=1;i<=n;i++)
		sum[i] = (sum[i-1]+p[i].c)%mod;//求前綴和 
	int l=1,r=n,sign=0;
	ll k = 2*p[1].a - p[1].b;
	while(l <= r){
		int mid = (l+r)>>1;
		ll t = 2*p[mid].a - p[mid].b;
		if((t+k) <= 0)
			sign = mid,l = mid+1;
		else
			r = mid-1;
	}//求出最右邊下標
	ll res = 0;//two pointer
	for(l=1,r=sign;l<=r;l++){
		while(l<=r && (2ll*p[l].a + 2ll*p[r].a - p[l].b - p[r].b) > 0)
			r--;
		if(l > r)
			break;
		ll temp = (sum[r]-sum[l-1]+mod)%mod;
		res = (res + p[l].c * temp % mod)%mod;
	}
	return res;
}

int main(){
	std::ios::sync_with_stdio(false);
	cin >> n;
	for(int i=1;i<=n;i++)
		cin >> p[i].a >> p[i].b >> p[i].c;
	ll ans = solve();
	for(int i=1;i<=n;i++)
		swap(p[i].a,p[i].b);
	ans += solve();
	ans %= mod;
	cout << ans << endl;
	return 0;
}	

I: Gree 的心房

在這裏插入圖片描述
這道簽到題也卡了我一會,原來是數據量卡了我,沒有用long long,最後幾個數據過不了,這題的思路就是求最短路,關於最短路,在這裏我們可以知道沿着邊緣從左上角走到右下角最短,即(n-1)+(m-1),然後關於障礙物,當取得最短路的時候,障礙物最多可以放(n-1)(m-1)個,因爲障礙物是隨機放的,也沒要求我們輸出障礙物位置,我們就可以直接判斷,其負責度爲O(1)。
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
long long ans = n+m-2;
if(k > (n*m)-ans-1)
printf("-1");
else
printf("%lld",ans);
return 0;
}

題外話

今天還是很自閉的,畢竟一直卡着,5個小時只做出2題,哎,還是自己做的題目不夠多,太菜了。也感覺最近的生活有些累,還是得好好緩一下,加油!晚安。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章