夢熊2024--四月份--基礎演算法組

学贵坚持發表於2024-04-09

A

link

這個題是一個暴力
判斷是否全在對角線上或下,兩次二重迴圈即可,如果是,直接乘起來。

點選檢視程式碼
#include<bits/stdc++.h>

#define int long long

using namespace std;

const int mo = 1e9+7;

int n;
int a[805][805];

bool x(){
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j < i;++ j){
			if(a[i][j] != 0) return false;
		}
	return true;
}

bool s(){
	for(int i = 1;i <= n;++ i)
		for(int j = i+1;j <= n;++ j){
			if(a[i][j] != 0) return false;
		}
	return true;
}

signed main(){
	
	cin >> n;
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= n;++ j)
			cin >> a[i][j];
	
	int ans = 1;
	if(x()||s()){
		for(int i = 1;i <= n;++ i){
			ans *= a[i][i];
			ans %= mo;
		}
		cout << ans;
	}
	else cout << "Arknights!";
	
	return 0;
	
}

B

link

這個題和\(ABC347\)\(c\)很像。
很顯然的一個性質,如果區間長度\(\geq T\),一定可以消滅。
不管是在哪個位置,對映到\(1\)~\(T\)(這時只剩長度\(\leq T\)的了)。
判斷那個位置能消耗的生命值最小,即可。

點選檢視程式碼
#include<bits/stdc++.h>

#define int long long

using namespace std;

int n,t,sum,ans;
int l[100005],r[100005],v[100005];
int cf[200005];
int cn;

signed main(){
	
	cin >> n >> t;
	for(int i = 1;i <= n;++ i){
		cin >> l[i] >> r[i] >> v[i];
		sum += v[i];
		if(r[i]-l[i]+1 >= t){
			cn += v[i];
			continue;
		} 
		int y = l[i];
		l[i] %= t;
		if(l[i] == 0) l[i] = t;
		r[i] -= (y-l[i]);
		r[i] = min(r[i],2*t);
		cf[l[i]] += v[i];
		cf[r[i]+1] -= v[i];
	}
	
	for(int i = 1;i <= 2*t;++ i)
		cf[i] += cf[i-1];
	
	for(int i = 1;i <= t;++ i)
		ans = max(ans,cf[i]+cf[i+t]);
	
	ans += cn;
	ans = sum-ans;
	
	cout << ans;
	
	return 0;
	
}

C

link

這個題很有意思。
他雖然說是區間長度大於等於\(k\),但其實最優區間長度一定可以等於\(k\)
於是只需要用單調佇列求最大最小值即可。
關於為什麼最優區間長度一定可以等於\(k\)
設已經等於\(k\)了,考慮向右增大區間。
如果讓最大值更大了,肯定更好,但是如果讓最小值變小了,不好。
考慮如果讓最大值更大了,我們可以把左邊也增大,最小值可能還會變小,很好。
所以最優區間長度一定可以等於\(k\)

點選檢視程式碼
#include<bits/stdc++.h>

#define int long long

using namespace std;

int n,k;
int a[100005];

int xq[100005];
int nq[100005];
int hx = 1,hn = 1,tx,tn;
int ans = -1e18;

void pux(int x){
	while(a[xq[tx]] > a[x]&&hx <= tx) tx--;
	xq[++tx] = x;
}

void pun(int x){
	while(a[nq[tn]] < a[x]&&hn <= tn) tn--;
	nq[++tn] = x;
}

signed main(){
	
	cin >> n >> k;
	for(int i = 1;i <= n;++ i)
		cin >> a[i];
	
	for(int i = 1;i < k;++ i)
		pux(i),pun(i);
	
	for(int i = k;i <= n;++ i){
		if(xq[hx] <= i-k) hx++;
		if(nq[hn] <= i-k) hn++;
		pux(i),pun(i);
		ans = max(ans,a[xq[hx]]+a[nq[hn]]);
	}
	
	cout << ans;
	
	return 0;
	
}

D

link

這個題是二分,考慮二分最大的費用,那麼每一個數到這個價值都是一個一定大小的區間,只要看看所有的區間夠不夠所有區間即可。
\(check\)函式是\(O(n)\)的。
最後算答案是可以看出,多出的區間一定是多算了最大費用,減去即可。

點選檢視程式碼
#include<bits/stdc++.h>

#define int long long

using namespace std;

int n,L;
int v[100005];

int check(int x){
	int sum = 0;
	for(int i = 1;i <= n;++ i){
		int d = x/v[i];
		sum += d;
	}
	return sum;
}

signed main(){
	
	cin >> n >> L;
	for(int i = 1;i <= n;++ i)
		cin >> v[i];
	
	sort(v+1,v+1+n);
	
	int l = 0ll,r = 1e18,md;
	while(l < r){
		md = (l+r)/2;
		if(check(md) >= L) r = md;
		else l = md+1;
	} 
	
	int sum = 0;
	for(int i = 1;i <= n;++ i){
		int d = r/v[i];
		sum += v[i]*d*(d+1)/2;
	}
	int q = check(r);
	int e = q-L;
	sum -= e*r;
	cout << sum;
	
	return 0;
	
}

相關文章