1003题
Wavy Tree
题目大意:
如果对于一个长度为 n n n ,下标从 1 1 1 开始的数组 a a a ,对于 ∀ i ∈ ( 1 , n ) \forall i \in (1, n) ∀i∈(1,n), 有 a [ i ] > m a x ( a [ i − 1 ] , a [ i + 1 ] ) a[i] > max(a[i - 1], a[i + 1]) a[i]>max(a[i−1],a[i+1]) 或者 a [ i ] < m i n ( a [ i − 1 ] , a [ i + 1 ] ) a[i] < min(a[i - 1], a[i + 1]) a[i]<min(a[i−1],a[i+1]) 成立,我们认为 a a a 数组是起伏不平的。
现在,给定一个长度为 n n n 且由正整数组成的数组 b b b ,你需要通过一下操作让数组 b b b 变得起伏不平:
- 选定一个位置 i i i,让 b i b_i bi 增大或者减小 1 1 1 。
以上操作每次需要消耗 1 1 1 个硬币,问:要使得数组 b b b 变得起伏不平,最少需要耗费多少个硬币?
思路:
通过分析,我们知道:当我们确定了前三个数的相对关系后,后续的所有数的相对关系也随之确定,代价的计算方式也是相同的。
换而言之,我们需要决定的是奇数位置填较大的数还是偶数位填较大的数。直观的看,若将处理后的 b b b 数组在二维坐标系上表示出来,并连线,就是一条波浪线,所以称数组 b b b 起伏不平。
通过分析,对于每三个数,我们注意到:我们若只修改后两个数字,代价将会更低。由于我们修改最后一个数时,只涉及到与第二个数之间的相对关系,换而言之,每次我们只需要贪心的考虑 b i − 1 b_{i - 1} bi−1 和 b i b_i bi 两个数之间的大小关系即可,每次只修改 b i b_i bi ,就能使得最终答案最优。
参考代码:
#include <iostream>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
ll b1[N], b2[N];
ll sum = 0;
void cal(ll a[], int i, bool flag) {
if(flag) {
if(a[i - 1] <= a[i])
sum += a[i] - a[i - 1] + 1, a[i] = a[i - 1] - 1;
} else {
if(a[i - 1] >= a[i])
sum += a[i - 1] - a[i] + 1, a[i] = a[i - 1] + 1;
}
}
void solve() {
int n; cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> b1[i]; b2[i] = b1[i];
}
ll ans = 2e18; sum = 0;
for(int i = 2; i <= n; i ++ )
if(i & 1) cal(b1, i, 1);
else cal(b1, i, 0);
ans = min(ans, sum);
sum = 0;
for(int i = 2; i <= n; i ++ )
if(i & 1) cal(b2, i, 0);
else cal(b2, i, 1);
ans = min(ans, sum);
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while(t -- ) {
solve();
}
return 0;
}
1007题
Even Tree Split
题目大意
给定一个长度为 n n n 的无向树,你需要删除其中的一些边,使得剩下的组件的节点数为偶数,求满足条件的删法数量
思路
对整个树进行深搜并计算相对应的节点数量,记录偶数节点的数量为 c n t cnt cnt ,那么答案就是 2 c n t − 1 2 ^ {cnt} - 1 2cnt−1
代码
#include<iostream>
#include<string.h>
#include<vector>
#define int long long
using namespace std;
const int N = 1e5+50;
const int mod = 998244353;
vector<int> g[N];
int cnt;
int sum[N];
void dfs(int u,int fa){
int len = (int)g[u].size();
sum[u] = 1;
for(int i = 0;i < len;i ++){
int v = g[u][i];
if(v == fa)continue;
dfs(v,u);
sum[u] += sum[v];
}
if(sum[u] % 2 == 0 && u != 1)cnt++;
}
void solve(){
memset(sum,0,sizeof sum);
int n;
cin>>n;
for(int i = 1;i <= n;i ++)g[i].clear();
for(int i = 0;i < n-1;i ++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
cnt = 0;
dfs(1,0);
int ans = 1;
for(int i = 1;i <= cnt;i ++){
ans = ans * 2 % mod;
}
cout<<ans-1<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--)solve();
system("pause");
return 0;
}
1009题
Painting Game
题目大意
A A A 和 B B B 轮流涂色,保证不能有两个相邻格子上色,且一直行动到无法在任何一个格子涂色为止, A A A 想让涂色最小化, B B B 想让涂色最大化,求最后上色的个数。
分析
由于需要一直涂色直到一方无法涂为止,所以在双方都采取最佳策略的情况下, A A A 的策略是在最右涂色位置的三格之后涂色,以确保中间两格一定不可使用; B B B 的策略时在最右涂色位置的四格后涂色,以确保中间的格子必须涂上,所以形成了一个 7 7 7 的循环节,每 7 7 7 个格子中有 3 3 3 个涂满。这样只需考虑小于 7 7 7 的情况,枚举一下即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
int T,n;
string str;
int a[7]={0,1,1,1,2,2,3},b[7]={0,1,1,2,2,3,3};
int main()
{
scanf("%d\n",&T);
while(T--)
{
scanf("%d ",&n);
cin>>str;
int ans=n/7*3;
if(str[0]=='A') ans+=a[n%7];
else ans+=b[n%7];
printf("%d\n",ans);
}
return 0;
}