//加了下面的代码,博客会禁止复制(代码还可以复制) // document.body.onselectstart = document.body.ondrag = function(){return false;}

Codeforces Round 889 (Div. 2)

C2. Dual (Hard Version)

题解

可以按照如下步骤实现:
1.得到一个足够大的正数或负数,这里的足够大指的是让它们的绝对值成为最大。

假设得到足够大的正数需要经过操作数为\(x_1\),得到足够大的负数需要经过操作数为\(x_2\),不难想到其中一个的值为0,对于另外一个,通过不断叠加自身,可以将步骤数控制在5步以内。因此可以得到\(x_1+x_2 \leq5\)

2.将足够大的数加到剩下的数中,使整体保持符号相同。

假设得到整体正数所需要的操作数为\(y_1\),得到整体负数所需要的操作数为\(y_2\),显然\(y_1+y_2\leq20\)

3.可以得到\(x_1 + x_2 + y_1 + y_2 \leq 25\),因此我们选择\(x_1 + y_1\)\(x_2 + y_2\)中的最小值,该最小值一定\(\leq12\)

4.对于正数来说,我们只要从前往后扫描数组,对每一个数加上前一个数即可保证单调不减,同理,对负数来说只需要从后往前加上后缀和即可。

code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
#define ff first
#define se second
#define pii pair<ll,ll>
const int maxn = 1e5+5;
void solve(){
	int n;
	cin >> n;
	vector<int>a(n+1);
	vector<pii>ans1, ans2;
	int sum1 = 0, sum2 = 0;
	int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
	int cnt1 = 0, cnt2 = 0;
	int idx = 0, idy = 0;
	int maxx = -25, minn = 25;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	for(int i = 1; i <= n; ++i){
		if(a[i] < minn){
			idy = i;
			minn = a[i];
		}
		if(a[i] > maxx){
			idx = i;
			maxx = a[i];
		}
		if(a[i] < 0)
			y1++;
		if(a[i] > 0)
			y2++;
	}
	if(minn + maxx < 0){
		if(maxx > 0){
			while(minn + maxx < 0){
				ans1.pb({idx,idx});
				x1++;
				maxx += maxx;
			}
			cnt1 += x1;
			for(int i = 1; i <= n; ++i){
				if(a[i] < 0){
					ans1.pb({i,idx});
				}else if(a[i] > 0){
					ans2.pb({i,idy});
				}
			}
			cnt1 += y1;
			cnt1 += n-1;
			cnt2 += (y2 + n-1);
			if(cnt1 < cnt2){
				cout << cnt1 << '\n';
				for(auto  it : ans1){
					cout << it.ff << ' ' << it.se << '\n';
				}
				for(int i = 2; i <= n; ++i)
					cout << i << ' ' << i-1 << '\n';
			}else{
				cout << cnt2 << '\n';
				for(auto it : ans2){
					cout << it.ff << ' ' << it.se << '\n';
				}
				for(int i = n-1; i >= 1; --i){
					cout << i << ' ' << i + 1 << '\n';
				}
			}
		}else{
			cout << n-1 << '\n';
			for(int i = n-1; i >= 1; --i){
				cout << i << ' ' << i+1 << '\n';
			}
		}
	}else{
		if(minn < 0){
			while(minn + maxx > 0){
				ans2.pb({idy,idy});
				x2++;
				minn += minn;
			}
			cnt2 += x2;
			for(int i = 1; i <= n; ++i){
				if(a[i] > 0){
					ans2.pb({i,idy});
				}else if(a[i] < 0){
					ans1.pb({i,idx});
				}
			}
			cnt2 += y2;
			cnt2 += n-1;
			cnt1 += (y1 + n-1);
			if(cnt2 < cnt1){
				cout << cnt2 << '\n';
				for(auto  it : ans2){
					cout << it.ff << ' ' << it.se << '\n';
				}
				for(int i = n-1; i >= 1; --i)
					cout << i << ' ' << i+1 << '\n';
			}else{
				cout << cnt1 << '\n';
				for(auto it : ans1){
					cout << it.ff << ' ' << it.se << '\n';
				}
				for(int i = 2; i <= n; ++i){
					cout << i << ' ' << i - 1 << '\n';
				}
			}
		}else{
			cout << n-1 << '\n';
			for(int i = 2; i <= n; ++i){
				cout << i << ' ' << i-1 << '\n';
			}
		}
	}
}
int main(){
	int t;
	cin >> t;
	while(t--){
		solve();
	}
	return 0;
}

D. Earn or Unlock

题解

\(dp[i]\)表示以第i个物品结尾时所能得到的最大分数

只需要考虑此时第i个物品是否已经解锁即可。

我们可以用二进制来保存每一只种状态。因此可以考虑用bitset来进行优化。

code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
#define ff first
#define se second
#define pii pair<ll,ll>
const int maxn = 1e5+5;
int a[maxn];
ll s[2*maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	cin >> n;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		s[i] = s[i-1] + a[i];
	}
	bitset<2*maxn>dp;
	dp.set(a[1]);
	dp.reset(0);
	ll ans = a[1];
	for(int i = 2; i <= n; ++i){
		if(dp.test(i-1)){
			ans = max(ans, s[i] - (i-1));
		}
		dp |= dp << a[i];
		dp.reset(i-1); 
	}
	for(int i = n + 1; i <= 2 * n; ++i){
		if(dp.test(i-1)){
			ans = max(ans, s[n] - (i-1));
		}
	}
	cout << ans;
	return 0;
}

E. Expected Destruction

题解

要使集合为空,则需要保证相邻两数两两合并。
定义\(dp[i][j]\)为将\(i,j(i<j)\)合并为一个数的期望操作次数,则有状态转移方程

\[dp[i][j] = \frac{dp[i+1][j]+1+dp[i][j+1]}{2} \]

\(i = j\)时,\(dp[i][j] = 0\)。当\(j=m+1\)时,\(dp[i][j] = m-i+1\)
答案即为\(\sum dp_{a_i,a_{i+1}}\)

code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
#define ff first
#define se second
#define pii pair<ll,ll>
const int maxn = 1e5+5;
const ll mod = 1000000007;
ll a[505];
ll dp[505][505];
ll quickpow(ll a, ll n){
	ll res = 1;
	while(n > 0){
		if(n & 1)
			res = (res * a) % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n, m;
	ll sum = 0;
	cin >> n >> m;
	ll inv2 = quickpow(2, mod-2);
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
	}
	a[n+1] = m + 1;
	for(int i = 1;i <= m; ++i)
		dp[i][m+1] = m+1-i;
	for(int i = m; i>=1; --i){
		for(int j = m; j > i; --j){
			dp[i][j] = inv2*((dp[i+1][j]+1)%mod+dp[i][j+1]%mod) % mod;
		}
	}
	for(int i = 1; i <= n; ++i){
		sum = (sum + dp[a[i]][a[i+1]]) % mod;
	}
	cout << sum;
	return 0;
}

这道题还可以从反面来考虑,对于每一个数来说,在互不相遇的情况下,共需要消耗的操作数为\(\sum(m+1-a_i)\),所以可以定义\(dp[i][j]\)为值为\(i\)和值为\(j\)的数相遇时到移出集合还需要的期望操作数,答案即为\(\sum(m+1-a_i)-\sum{dp_{a_{i},a_{i+1}}}\)

code
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
#define ff first
#define se second
#define pii pair<ll,ll>
const int maxn = 1e5+5;
const ll mod = 1000000007;
int m, n;
int a[505];
int vis[505][505],f[505][505];
ll inv2;
ll quickpow(ll a, ll n){
	ll res = 1;
	while(n > 0){
		if(n & 1)
			res = (res * a) % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res;
}
ll dp(int x, int y){
	if(x == y){
		return m+1-x;
	}
	if(y == m + 1)
		return 0;
	if(vis[x][y])
		return f[x][y];
	vis[x][y] = 1;
	f[x][y] = (inv2 * dp(x+1,y) + inv2*dp(x,y+1)) % mod;
	return f[x][y];
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin >> n >> m;
	inv2 = quickpow(2, mod-2);
	ll ans = 0;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	for(int i = 1; i <= n; ++i){
		ans = (ans + m+1-a[i]) % mod;
	}
	for(int i = 1; i < n; ++i){
		ans  =  (ans - dp(a[i],a[i+1]) + mod) % mod;
	}
	cout << ans;
	return 0;
}
posted @ 2023-08-01 15:48  龙鳞墨客  阅读(77)  评论(0)    收藏  举报