10.18测试
T1 Bug
题目描述
A 君在机缘巧合下得到了一把养蛊神器,于是 A 君希望培养出迄今为止战斗力最强的 Bug。A 君把现有的 \(n\) 个 Bug 排成一个序列 \(a_1, a_2, \dots, a_n\),其中 \(a_i\) 表示第 \(i\) 个 Bug 的战斗力。A 君需要重复进行以下操作直到只剩下一个 Bug:
- 选择一个端点(指最左边或者最右边)的 Bug,删除它。
- 选择一个非端点的 Bug,将它的战斗力变为当前它左右两个相邻 Bug 的战斗力之和,然后删除左右两边的 Bug。
求操作结束后剩下的一个 Bug 的最高战斗力是多少,并求出需要的最少操作数。
输入格式
第一行一个整数,表示 \(n\)。
第二行 \(n\) 个整数,第 \(i\) 个整数表示 \(a_i\)。
输出格式
第一行一个整数,表示操作结束后剩下的 Bug 的最高战斗力。
第二行一个整数,表示需要的最少操作数。
输入输出样例 #1
输入 #1
6
-1 5 2 -2 3 -3
输出 #1
5
4
输入输出样例 #2
输入 #2
4
-2 -4 -1 -3
输出 #2
-1
3
输入输出样例 #3
输入 #3
10
32644 -36604 -178874 -98683 92567 -272835 -35544 -151678 -8486 -197803
输出 #3
125211
5
数据规模与约定
| 测试点 | \(n\le\) | 分值 |
|---|---|---|
| \(1\sim 2\) | \(10\) | \(10\) |
| \(3\sim 6\) | \(2000\) | \(20\) |
| \(7\sim 20\) | \(10^6\) | \(70\) |
对于 \(100\%\) 的数据,满足 \(-10^{10}\le a_i\le 10^{10}\)。
Sol
并不难的贪心。显然区分奇数位和偶数位即可。
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int M=1e6+5;
ll a[M];
int f(int x){
if(x==0)return 0;
return x/2+1;
}
int main(){
int n,flag=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>0)flag=1;
}
if(flag==0){
ll maxn=-1e18;
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i]>maxn){
maxn=a[i];
cnt=f(i-1)+f(n-i);
}
else if(a[i]==maxn)cnt=min(cnt,f(i-1)+f(n-i));
}
cout<<maxn<<endl<<cnt;
return 0;
}
ll s1=0,s2=0;
int lst1=0,cnt1=0,lst2=0,cnt2=0;
for(int i=1;i<=n;i++){
if(i%2==0){
s1+=max(0ll,a[i]);
if(a[i]>=0){
if(!lst1)cnt1+=f(i-1);
else cnt1+=f(i-lst1-1);
lst1=i;
}
}else{
s2+=max(0ll,a[i]);
if(a[i]>=0){
if(!lst2)cnt2+=f(i-1);
else cnt2+=f(i-lst2-1);
lst2=i;
}
}
}
cnt1+=f(n-lst1);
cnt2+=f(n-lst2);
if(s1>s2)cout<<s1<<"\n"<<cnt1;
else if(s1<s2)cout<<s2<<"\n"<<cnt2;
else cout<<s1<<"\n"<<min(cnt1, cnt2);
return 0;
}
T2 故障机器人
题目描述
事态紧急,有 \(k\) 名杀戮机器人正在寻找你,你需要规划一条逃生路径。而你目前所处的地方有 \(n\) 个房间,你初始在 \(1\) 号房间,需要走到 \(n\) 号房间逃离。第 \(i\) 个杀戮机器人在 \(a_i\) 号房间(\(a_i \ne 1\),且 \(a_i\) 互不相等)。有 \(m\) 条走廊,每条走廊连接两个房间,可以通过走廊从一个房间走到另一个房间。走廊非常的黑暗,并且非常宽敞,所以你在走廊内不会被杀戮机器人发现,但房间中很明亮,所以你如果在某一时刻和没有故障的杀戮机器人处在同一个房间,你就会出局。
每一秒钟,你会选择一条连接当前所处房间的走廊并走到走廊尽头的另一个房间,机器人则是会 随机地 选择一条去走,并记录下它所经过的路径。比如一个机器人初始在 \(2\) 号房间,它第一秒通过 \(2\to 3\) 这条走廊到达 \(3\) 号房间,那么它的路径就是 \((2,3)\),然后下一秒它继续走 \(3 \to 4\) 这条走廊 ,那么路径就是 \((2,3)(3,4)\)。如果某一秒和前一秒走了同一条走廊(意味着它走过去以后又往回走了),那么路径会往前删掉一条记录,比如前面在路径为 \((2,3)(3,4)\) 的情况下如果走了 \(4 \to 3\) 往回,那么路径就会变成 \((2,3)\)。
注意我们并不关心是不是经过重复的点,我们只关心是否有连续的两秒,机器人走了相同的走廊。
好消息是,机器人如果路径中二元组的数量超过了一个阈值 \(d\),那么它就会变成故障机器人,它会在到达下一个房间之前损坏,并且再也无法检测到你。
比如当 \(d=2\) 时,机器人的路径为 \((2,3)(3,4)\) 的情况下,试图走 \(4\to 5\),就会在达到 \(5\) 号房间前损坏,并且失去威胁。
坏消息是,你并不知道机器人是如何进行 随机选择 的,所以你得保证自己规划的路径百分之百安全,也就是说在你选择的路上,无论机器人如何移动都不会找到你。
注意:你和机器人都不能选择待在原地不动;你在 \(n\) 号房间被机器人发现也会失败。(你得保证自己达到 \(n\) 号房间的时候里面没有机器人才能安全逃离)
输入格式
第一行三个正整数 \(n,m,d\)。
接下来 \(m\) 行,第 \(i\) 行 \(2\) 个正整数 \(u_i,v_i\),描述了一条走廊连接 \(u_i\) 和 \(v_i\) 两个房间。(保证没有自环和重边)
然后一行一个正整数 \(k\),表示机器人数量。
接下来一行 \(k\) 个正整数 \(a_i\),表示第 \(i\) 个机器人所在的初始房间号。
输出格式
如果存在一条百分百能逃出的路径,那么第一行输出一个正整数 \(L\),表示需要经过的走廊数量,然后第二行输出 \(L+1\) 个整数,描述你所规划的路径(如果有多种方案,优先输出长度最短的;如果依然有多种方案,输出倒数第二步所在节点编号最小的;如果仍然有多种方案,则输出倒数第三步所在节点编号最小的,以此类推)。
否则直接输出 -1 即可。
输入输出样例 #1
输入 #1
7 8 2
1 2
2 3
3 7
2 5
5 6
3 6
1 4
4 5
1
5
输出 #1
-1
输入输出样例 #2
输入 #2
7 8 2
1 2
2 3
3 7
2 5
5 6
3 6
1 4
4 5
1
4
输出 #2
3
1 2 3 7
说明/提示
| 测试点 | \(n,d\le\) | $m\le $ | $k\le $ | 特殊性质 | 分值 |
|---|---|---|---|---|---|
| \(1\sim 2\) | \(10\) | \(40\) | \(1\) | \(10\) | |
| \(3\sim 4\) | \(100\) | \(400\) | \(3\) | \(10\) | |
| \(5\sim 6\) | \(10^5\) | \(5\times 10^5\) | \(1\) | \(10\) | |
| \(7\sim 10\) | \(10^5\) | \(5\times 10^5\) | \(10^5\) | 保证是菊花图,且 \(n\) 在中心点 | \(20\) |
| \(11\sim 20\) | \(10^5\) | \(5\times 10^5\) | \(10^5\) | \(50\) |
对于 \(100\%\) 的数据,保证 \(1\le a_i\le n\)。
Sol
\(hfu\)最爱的蓝白点思想。。。
扩展机器人能走的\(d\)步数范围即可。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 5e5+5;
int n, m, d, k;
int a[N];
vector<int> g[N];
int dis[N][2]; //dis[i][0/1] 经过 偶数/奇数 条边到达当前点的最早时间
queue<pii> q;
void bfs()
{
for(int i=1;i<=k;i++)
dis[a[i]][0] = 0, q.push({a[i], 0});
while(q.size())
{
int t = q.front().first, edge = q.front().second;
q.pop();
for(int i:g[t])
{
if(edge==d)
continue;
if(dis[i][(edge+1)%2]>dis[t][edge%2]+1)
dis[i][(edge+1)%2] = dis[t][edge%2]+1, q.push({i, edge+1});
}
}
}
int dd[N][2];
int route[N][2];
void bfs2()
{
q.push({1, 0});
memset(dd, 0x3f, sizeof dd);
dd[1][0] = 0;
while(q.size())
{
int t = q.front().first, edge = q.front().second;
q.pop();
for(int i:g[t])
{
if(dis[i][(edge+1)%2]>dd[t][edge%2]+1)
{
if(dd[i][(edge+1)%2]>dd[t][edge%2]+1)
{
dd[i][(edge+1)%2] = dd[t][edge%2] + 1;
q.push({i, edge+1});
route[i][(edge+1)%2] = t;
}
else if(dd[i][(edge+1)%2]==dd[t][edge%2]+1)
route[i][(edge+1)%2] = min(route[i][(edge+1)%2], t);
}
}
}
}
void work(int u, int edge)
{
if(u==0)
return;
work(route[u][edge%2], edge^1);
cout<<u<<" ";
}
int main()
{
cin>>n>>m>>d;
for(int i=1;i<=m;i++)
{
int u, v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++)
sort(g[i].begin(), g[i].end());
memset(dis, 0x3f, sizeof dis);
cin>>k;
for(int i=1;i<=k;i++)
cin>>a[i];
bfs();
bfs2();
int t = min(dd[n][0], dd[n][1]);
if(t==0x3f3f3f3f)
puts("-1");
else
{
cout<<t<<"\n";
work(n, t%2);
}
return 0;
}
T3 分橘子
题目描述
有 \(n\) 个橘子,编号为 \(1\) 到 \(n\)。第 \(i\) 个橘子的重量为 \(w_i\)。Alice 和 Bob 将按照以下方式分橘子:
-
首先选择一个 $ (1,2,\cdots,n) $ 的排列 $ (p_1,\ p_2,\ \cdots,\ p_n) $。
-
接下来,对于 \(i=1,2,\cdots,n\),依次进行以下操作:
- 如果 Alice 已经拿到的橘子总重量小于等于 Bob 已经拿到的橘子总重量,则 Alice 拿橘子 \(p_i\)。否则,Bob 拿橘子 \(p_i\)。
请你求出有多少种排列 \(p\),使得最终两人拿到的橘子总重量相等?答案对 \(998244353\) 取模。
输入格式
第一行一个正整数 \(n\)。
第二行 \(n\) 个正整数 \(w_i\)。
输出格式
一行一个整数,表示有多少种排列,使得最终两人拿到的橘子总重量相等。
输入输出样例 #1
输入 #1
3
1 1 2
输出 #1
4
输入输出样例 #2
输入 #2
4
1 2 3 8
输出 #2
0
输入输出样例 #3
输入 #3
20
2 8 4 7 5 3 1 2 4 1 2 5 4 3 3 8 1 7 8 2
输出 #3
373835282
说明/提示
| 测试点 | \(n\le\) | $w_i\le $ | 特殊性质 | 分值 |
|---|---|---|---|---|
| \(1\sim 2\) | \(10\) | \(10\) | \(10\) | |
| \(3\) | \(200\) | \(100\) | 总重量之和为奇数 | \(5\) |
| \(4\sim 8\) | \(100\) | \(100\) | \(25\) | |
| \(9\sim 20\) | \(200\) | \(100\) | \(60\) |
Sol
只能用dp。论我用\(next\)_\(permutation\)混了\(25pts\)(雾
忘了dp怎么推导的了
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxN = 200 + 10;
const int mod = 998244353;
int f[2][maxN][maxN * maxN];
int n,a[maxN],jc[maxN];
int main(){
scanf("%d",&n);
int sumh = 0;
jc[0] = 1;
for(int i = 1 ; i <= n ; ++i){
scanf("%d",a + i);
sumh += a[i];
jc[i] = 1ll * jc[i - 1] * i % mod;
}
if(sumh & 1){
puts("0");
return 0;
}
sumh = sumh >> 1;
f[0][0][0] = 1;
for(int i = 1,op,opt ; i <= n ; ++i){
op = i & 1;
opt = op ^ 1;
for(int j = 0 ; j <= i ; ++j){
for(int k = 0 ; k <= sumh ; ++k){
f[op][j][k] = f[opt][j][k];
if(j > 0 && k >= a[i])
f[op][j][k] = (f[op][j][k] + f[opt][j - 1][k - a[i]]) % mod;
}
}
}
long long ans = 0;
int op = n & 1;
for(int i = 1 ; i <= n ; ++i){
ans = (1ll * jc[i] * jc[n - i] % mod * f[op][i][sumh] % mod + ans) % mod;
}
cout << ans << endl;
return 0;
}
T4 扰乱神器
题目描述
D 君是潜伏在 Mars 星的间谍,D 君携带着从母星带来的扰乱神器,用于扰乱 Mars 星的晶体防御序列。Mars 星的晶体防御序列由 \(2^n\) 块晶体组成,第 \(i\) 块晶体的能量为 \(p_i\)。晶体防御序列的扰乱度定义为序列中的逆序对数,由于 Mars 星的晶体存在自适应能力,如果某个固定的防御序列持续过久,该序列的扰乱度就不再对晶体防御序列造成威胁了。因此 D 君对 Mars 星的晶体防御序列进行了 \(m\) 次扰乱,第 \(i\) 次扰乱可以用 \(q_i, l_i, r_i\) 表示:
- 将晶体防御序列分为 \(2^{q_i}\) 个大小为 \(2^{n-q_i}\) 的块。
- 选择第 \(l_i\) 到第 \(r_i\) 个块。
- 对于每个选择的块, 将其翻转。
D 君想知道每次扰乱操作后, 整个晶体防御序列的扰乱度。
输入格式
第一行为两个正整数 \(n, m\)。
第二行 \(2^n\) 个整数 \(p_1, p_2, \cdots p_{2^n}\),为给定的晶体防御序列。
接下来 \(m\) 行,每行 3 个非负整数 \(q_i, l_i, r_i\),表示一次扰乱操作。
输出格式
\(m\) 行,每行一个整数,为每次操作后晶体防御序列的扰乱度。
输入输出样例 #1
输入 #1
3 10
7 3 3 3 8 6 5 3
1 1 1
2 2 2
2 1 3
1 1 2
0 1 1
2 2 2
0 1 1
0 1 1
1 1 1
1 1 2
输出 #1
9
10
8
7
15
14
8
14
12
17
说明/提示
| Subtask | \(n\le\) | $m\le $ | 特殊性质 | 分值 |
|---|---|---|---|---|
| \(1\) | \(5\) | \(10\) | \(10\) | |
| \(2\) | \(10\) | \(1000\) | \(10\) | |
| \(3\) | \(20\) | \(10^5\) | \(l_i=1,r_i=2^{q_i}\) | \(20\) |
| \(4\) | \(20\) | \(10^5\) | \(l_i=r_i\) | \(20\) |
| \(5\) | \(20\) | \(10^5\) | \(40\) |
对于 \(100\%\) 的数据,\(1 \leq n \leq 20, 1 \leq p_i \leq 2^n, 1 \leq m \leq 10^5, 0 \leq q_i < n, 1 \leq l_i \leq r_i \leq 2^{q_i}\)。
Sol
线段树翻转几乎板子,但我不会(((
先做模版,后面再补

浙公网安备 33010602011771号