1. 程式人生 > 其它 >牛客練習賽75 簡要題解

牛客練習賽75 簡要題解

技術標籤:# 牛客

A-廣義肥波

尤拉降冪。

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 1e9 + 7 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
using namespace std ;
struct Easymath
{
	ll qpow(ll a , ll b) //快速冪
	{
		if(b < 0)  return 0 ;
		ll ans = 1 ; 
		a %= mod ;
		while(b)
		{
			if(b & 1)  ans = (ans * a) % mod ;
			b >>= 1 , a = (a * a) % mod ;
		}
		return ans % mod ;
	}
	ll ksc_log(ll x , ll y , ll mod) //快速乘
	{
		x %= mod , y %= mod ;
		ll ans = 0;
		while(y)
		{
			if(y & 1) ans = (ans + x) % mod ; 
			y >>= 1 ;
			x = (x + x) % mod ;
		}
		return ans;
	}
	ll ksc_O1(ll x , ll y , ll mod) //快速乘
	{
		x %= mod , y %= mod ;
		ll z = (ld)x * y / mod ;
		ll ans = x * y - z * mod ;
		if(ans < 0)  ans += mod ;
		else if(ans >= mod)  ans -= mod ;
		return ans ;
	}
	int cnt = 0 ;
	bool vis[maxn] ;
	int prime[maxn] ;
	void get_prime(int up) //素數篩
	{
		memset(vis , 0 , sizeof(vis)) ;
		vis[1] = 1 ;
		for(int i = 2 ; i <= up ; i ++)
		{
			if(!vis[i]) 
			prime[++ cnt] = i ;
			for(int j = 1 ; j <= cnt && i * prime[j] <= up ; j ++)
			{
				vis[i * prime[j]] = 1 ;
				if(i % prime[j] == 0) break ;
			}
		}
	}
	//begin 判定大素數
	ll mul(ll a , ll b , ll mod)
	{
		ll ret = 0 ;
		while(b) 
		{
			if(b & 1)  ret = (ret + a) % mod ;
			a = (a + a) % mod ;
			b >>= 1 ;
		}
		return ret ;
	}
	ll pow(ll a , ll b , ll mod)
	{
		ll ret = 1 ;
		while(b) 
		{
			if(b & 1)  ret = mul(ret , a , mod) ;
			a = mul(a , a , mod) ;
			b >>= 1 ;
		}
		return ret ;
	}
	bool check(ll a , ll n)
	{
		ll x = n - 1 ;
		int t = 0 ;
		while((x & 1) == 0) 
		{
			x >>= 1 ;
			t ++ ;
		}
		x = pow(a , x , n) ;
		ll y ;
		rep(i , 1 , t)
		{
			y = mul(x , x , n) ;
			if(y == 1 && x != 1 && x != n - 1)  return 1 ;
			x = y ;
		}
		if(y != 1) return 1 ;
		return 0 ;
	}
	bool Miller_Rabin(ll n) 
	{
		if(n == 2)  return 1 ;
		if(n == 1 || !(n & 1))  return 0 ;
		const int arr[12] = {2,3,5,7,11,13,17,19,23,29,31,37} ;
		rep(i , 0 , 11) 
		{
			if(arr[i] >= n) break ;
			if(check(arr[i] , n)) return 0 ;
		}
		return 1 ;
	}
	//end 判定大素數
	ll get_inv(ll x) //逆元
	{
		return qpow(x , mod - 2) % mod ;
	}
	ll inv1[maxn] ;  //乘法逆元
	void init1(int up)
	{
		inv1[1] = 1 ;
		for(int i = 2 ; i <= up ; i ++)
		inv1[i] = (ll)(mod - mod / i) * inv1[int(mod % (ll)i)] % mod ;
	}
	ll fac[maxn] ;
	ll inv[maxn] ; //階乘逆元
	void init(int up) 
	{
		fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
		for(int i = 2 ; i <= up ; i ++)
		{
			fac[i] = fac[i - 1] * i % mod ;
			inv[i] = -inv[mod % i] * (mod / i) % mod ;
			while(inv[i] < 0) inv[i] += mod ;
		}
		for(int i = 2 ; i <= up ; i ++)  
		inv[i] = inv[i] * inv[i - 1] % mod ;
	}
	ll C(int n , int m)
	{
		return fac[n] * inv[m] % mod * inv[n - m] % mod ;
	}
} em ;
int main()
{
	ios ;
	ll a , b , m , n ;
	cin >> a >> b >> m >> n ;
	vector<ll> f(n + 1 , 0) ;
	f[1] = 1 ;
	f[2] = 1 ; 
	for(int i = 3 ; i <= n ; i ++)  f[i] = (a * f[i - 1] % (mod - 1) + b * f[i - 2] % (mod - 1)) % (mod - 1) ;
	cout << em.qpow(m , f[n]) << '\n' ;
	return 0 ;
}

B-小D和他的魔法石

n=2時,操作策略是唯一的。

n>2時,只要使抗力最小的魔法石的魔力最大,然後貪心即可。

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 1e9 + 7 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
using namespace std ;
int main()
{
	ios ;
	ll n , m , k ;
	cin >> n >> m >> k ;
	vector<pll> c(n) ;
	for(int i = 0 ; i < n ; i ++)  cin >> c[i].fi ;
	for(int i = 0 ; i < n ; i ++)  cin >> c[i].se ;
	function<ll()> cal = [&]()
	{
		ll res = 0 ;
		vector<ll> dp(m + 1 , -1e18) ;
		dp[0] = 0 ;
		for(int i = 0 ; i < n ; i ++)
		  for(int j = c[i].fi ; j <= m ; j ++)
		    dp[j] = max(dp[j] , dp[j - c[i].fi] + c[i].se) , res = max(res , dp[j]) ;
		return res ;
	} ;
	sort(c.begin() , c.end()) ;
	ll ans = 0 ;
	if(n == 2)  
	{
		if(k % 2 == 1)  swap(c[0].se , c[1].se) , ans = cal() , swap(c[0].se , c[1].se) ;
		else  ans = cal() ;
	}
	else
	{
		ans = max(ans , cal()) ;
		if(k >= 1)
		{
			ll mx = c[0].se ;
			for(int i = 1 ; i < n ; i ++)  mx = max(mx , c[i].se) ;
			ans = max(ans , m / c[0].fi * mx) ;
		}
	}
	cout << ans << '\n' ;
	return 0 ;
}

C-寶石街

從第l個位置開始撿寶石,到第r個位置結束撿寶石。那麼[l+1,r]的位置的寶石全部取,第l個位置的寶石取一部分。雙指標即可。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
	std::ios::sync_with_stdio(false) ;
	std::cin.tie(0) ;
	long long n , t , type ;
	cin >> n >> t >> type ;
	vector<long long> a(n) ;
	if(type == 1)
	{
		for(int i = 0 ; i < n ; i ++)  cin >> a[i] ;
	}
	else
	{
		long long p ;
		cin >> a[0] >> p ;
		for(int i = 1 ; i < n ; i ++)
		{
			long long x = (a[i - 1] ^ (a[i - 1] << 13)) ;
			long long y = (x ^ (x >> 17)) ;
			a[i] = (y ^ (y << 5)) % p + 1 ;
		}
	}
	long long ans = 0 ;
	int j = n - 1 ;
	long long res = a[n - 1] ;
	long long sum = 0 ;
	for(int i = n - 1 ; i >= 0 ; i --)
	{
		if(j > i)  j -- , assert(res == 0 && sum == 0) , res = a[j] ;
		while(j - 1 >= 0 && sum + a[j - 1] * (i - (j - 1)) <= t)  j -- , res += a[j] , sum += a[j] * (i - j) ;
		if(j - 1 >= 0)
		{
			long long num = (t - sum) / (i - (j - 1)) ;
			ans = max(ans , res + min(a[j - 1] , num)) ;
		}
		ans = max(ans , res) ;
		res -= a[i] ;
		sum -= res ;
	}
	cout << ans << '\n' ;
	return 0 ;
}

D-減數遊戲

結論是每次取兩個最小的數a,b。但是直接模擬需要高精度。考慮如果不用高精度依然可以表示相對大小。設原序列升序排列後得到[a,b,a',b',...,p]。最大的數是p,那麼當a\times b + k \geqslant p時,顯然a' \times b' + k \geqslant a \times b + k。則每次取序列最靠前的兩個數,合併後塞到隊尾即可。可以取模,也保證了相對大小。

#include<bits/stdc++.h>
using namespace std ;
const int mod = 1e9 + 7 ;
int main()
{
	std::ios::sync_with_stdio(false) ;
	std::cin.tie(0) ;
	int n , k ;
	long long mx = 0 ;
	cin >> n >> k ;
	vector<long long> a(n) ;
	for(int i = 0 ; i < n ; i ++)  cin >> a[i] , mx = max(mx , a[i]) ;
	priority_queue<long long , vector<long long> , greater<long long>> q ;
	for(int i = 0 ; i < n ; i ++)  q.push(a[i]) ;
	while(q.size() > 1)
	{
		long long a = q.top() ;
		q.pop() ;
		long long b = q.top() ;
		q.pop() ;
		q.push(a * b + k) ;
		if(a * b + k >= mx)  break ;
	}
	deque<long long> dq ;
	while(!q.empty())  dq.push_back(q.top()) , q.pop() ;
	while(dq.size() > 1)
	{
		long long a = dq.front() % mod ;
		dq.pop_front() ;
		long long b = dq.front() % mod ;
		dq.pop_front() ;
		dq.push_back((a * b + k) % mod) ;
	}
	cout << dq.front() % mod << '\n' ;
	return 0 ;
}

E-炒雞礦工

dp[i][j]表示當前處於第i級,花費時間是j的最大金礦重量。

轉移方程dp[i][j] = max(dp[i - 1][j] - w[i] , dp[i - 1][j - k * s[i]] + k * v[i])

具體寫程式碼時不用列舉k,正著掃一遍就行了。時間複雜度O(n^2)

#include<bits/stdc++.h>
using namespace std ;
const int mod = 1e9 + 7 ;
int main()
{
	std::ios::sync_with_stdio(false) ;
	std::cin.tie(0) ;
	long long p , c , n , m , t ;
	cin >> p >> c >> n >> m >> t ;
	vector<long long> w(n + 1) ;
	vector<long long> v(n + 1) ;
	vector<long long> s(n + 1) ;
	for(int i = 1 ; i <= n ; i ++)  cin >> w[i] ;
	v[0] = c ;
	for(int i = 1 ; i <= n ; i ++)  cin >> v[i] , v[i] += v[i - 1] ;
	s[0] = p ;
	for(int i = 1 ; i <= n ; i ++)  cin >> s[i] ;
	vector<vector<long long>> dp(n + 1) ;
	for(int i = 0 ; i <= n ; i ++)  dp[i].resize(t + 1 , -1e18) ;
	dp[0][0] = m ;
	for(int i = 0 ; i <= n ; i ++)
	{
		if(i)  for(int j = 0 ; j <= t ; j ++)  if(dp[i - 1][j] >= w[i])  dp[i][j] = dp[i - 1][j] - w[i] ;
		for(int j = s[i] ; j <= t ; j ++)  if(dp[i][j - s[i]] >= 0)  dp[i][j] = max(dp[i][j] , dp[i][j - s[i]] + v[i]) ;
	}
	long long ans = 0 ;
	for(int i = 0 ; i <= n ; i ++)  for(int j = 0 ; j <= t ; j ++)  ans = max(ans , dp[i][j]) ;
	cout << ans << '\n' ;
	return 0 ;
}

F-迷路の小L

首先想到最後一條邊是最長的,然後一通dp預處理即可。不過細節太多,我就咕咕咕了。