Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)A~F
A. A Variety of Operations
题意:只能进行三种操作:1. 对于双方+k; 2.对于双方一方+k,一方-k; k可以取任意数, 初始两个数为{0,0}问最少几步操作可以变成{n,m},不成立-1
分析:如果n和m为{0,0},那么0步,根据题意可知,两数和只能为偶数,所以n+m为奇数时-1,否则如果n=m,输出1,否则输出2
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e2 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N][N], dp[N][N], f[N];
char g[N][N];
string str;
vector<int> vec[N], vec1[N];
void solve()
{
int a, b;
cin >> a >> b;
if (a == 0 && b == 0)
{
puts("0");
return;
}
if ((a + b) % 2 == 1)
{
puts("-1");
return;
}
if (a == b)
{
puts("1");
return;
}
puts("2");
}
int main()
{
t = 1;
cin >> t;
while(t--)
solve();
return 0;
}
B. Take Your Places!
题意:多组输入,给出一串序列,每次操作只能交换相邻的数值,问最少交换多少次后,任意相邻数均为一奇一偶的情况,不成立-1
分析:显然奇数和偶数的数量不能超过1,超过不可能合法,如果奇数的数量比偶数多,那么奇数必定会排坐标奇数位置,如果偶数的数量比奇数多,那么偶数会排在坐标奇数的位置,如果相等就都有可能,显然对于所有按位置排列的奇数,分别去对应位置会更优,比如奇数大于偶数的话,奇数按顺序必定会去到1,3,5,...,n的位置,所以统计原本位置和最终位置的绝对值的和就好了,奇数偶数数量相等的时候取和的最小值
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], dp[N], f[N];
char g[N];
string str;
vector<int> vec;
void solve()
{
vec.clear();
cin >> n;
int a = 0, b = 0, c = 0, d = 0;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
if (s[i] & 1)
{
a++;
vec.push_back(i);
}
else b++;
}
if (abs(a - b) > 1)
{
puts("-1");
return;
}
if (a == b)
{
int ans = 0, ans1 = 0;
for (int i = 0; i < vec.size(); i++)
{
ans += abs(vec[i] - (2*i+1));
ans1 += abs(vec[i] - (2*i+2));
}
printf("%d\n", min(ans, ans1));
return;
}
if (a > b)
{
int ans = 0;
for (int i = 0; i < vec.size(); i++)
ans += abs(vec[i] - (2*i+1));
printf("%d\n", ans);
return;
}
int ans = 0;
for (int i = 0; i < vec.size(); i++)
ans += abs(vec[i] - (2*i+2));
printf("%d\n", ans);
}
int main()
{
t = 1;
cin >> t;
while(t--)
solve();
return 0;
}
C. Compressed Bracket Sequence
题意:给出一串括号序列,以以下形式表示奇数位置是左括号的数量,偶数位置是右括号的位置,给出n个数(n<1000),每个数范围为1到1e9,问这些括号序列的连续子串的数量
分析:因为数据范围很小,所以暴力处理对于每个奇数位置去维护当前括号序列左括号处于该位置的情况数,对于多余的左括号,一旦用的话,只能用一次,所以维护左括号的最小值,跟最初一减就知道过程中用过几次,但是也可以不用多余的括号直接拼成就好比(3,1,2,3)(3,1,2,1,1,3)对于这两种情况,可以分成左右两边自成括号,也可以连接,所以用一个标记去维护左边有没有挡住的地方,能不能直接连到最左边的括号上
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e3 + 10, mod = 1e9 + 7;
int n, m, t, k;
ll s[N], dp[N], f[N];
char g[N];
string str;
vector<int> vec;
void solve()
{
cin >> n;
ll ans = 0;
for (int i = 1; i <= n; i++)
cin >> s[i];
for (int i = 2; i <= n; i += 2)
{
ans += min(s[i], s[i-1]);
if (s[i] > s[i-1]) s[i] -= s[i-1], s[i-1] = 0;
else s[i-1] -= s[i], s[i] = 0;
}
for (int i = 1; i < n; i += 2)
{
if (s[i+1]) continue;
ll u = s[i], v = s[i], minn = s[i];
ll bj = 0;
for (int j = i + 3; j <= n; j += 2)
{
bj += s[j-1] - s[j];
if (bj < 0) bj = 0;
if (bj == 0) ans++;
if (s[j])
{
u -= s[j];
minn = min(minn, u);
if (u < 0)
{
minn = 0;
break;
}
}
if (s[j-1]) u += s[j-1];
}
ans += v - minn;
// cout << ans << endl;
}
printf("%lld\n", ans);
}
int main()
{
t = 1;
// cin >> t;
while(t--)
solve();
return 0;
}
D. Take a Guess
题意:交互式题,你进行不超过2n次的查询,有两种查询:1.输出or i j,会查询到i|j;2.输出and i 就,会查询到i&j,最后输出:finnish 答案;给出n和k,询问这n个数中第k小的数
分析:2n次查询,显然可以有n次查询到i|j和i&j的和而i+j=(i|j)+(i&j),而对于3个数的话,知道a+b,b+c,c+a,就可以分析出a,b,c的值了,对于后续i可以查询第一个数和第i个数的情况,这样就知道了n个数分别是什么,sort排序一下就好了,对于第k小的数是可以做到O(n)复杂度的,但没必要
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], dp[N], f[N];
char g[N];
string str;
vector<int> vec;
void solve()
{
int a, b, c, d;
cin >> n >> m;
for (int i = 2; i <= n; i++)
{
cout << "and " << i - 1 << ' ' << i << endl;
cin >> a;
cout << "or " << i - 1 << ' ' << i << endl;
cin >> b;
s[i] = a + b;
}
cout << "and " << 1 << ' ' << 3 << endl;
cin >> a;
cout << "or " << 1 << ' ' << 3 << endl;
cin >> b;
s[1] = a + b;
a = s[1], b = s[2], c = s[3], d = (1ll * s[1] + s[2] + s[3]) / 2;
a = d - a, b = d - b, c = d - c;
s[1] = c, s[2] = a, s[3] = b;
for (int i = 4; i <= n; i++) s[i] -= s[i-1];
sort(s + 1, s + n + 1);
cout << "finish " << s[m] << endl;
}
int main()
{
t = 1;
// cin >> t;
while(t--)
solve();
return 0;
}
E. Equilibrium
题意:给出长度为n的两组数,q组查询,每次询问操作过后使得两组数l到r区间相等最小操作数,可进行的操作是选定偶数个位置,对于其中奇数的位置在第一个数组上+1,偶数的位置在第二个数组上+1,不成立输出-1
分析:显然对于任意的操作,在区间内和都不会改变,所以要保证区间内两组的和相同,否则必定不成立,对于区间内任意前i项(第二组数-第一组数)的和都一定要为非负数,如果有负数的存在意味着当处理到一定程度后,两组数不相等的第一个数的位置,第一组的数值大于第二组的数值,讲无论如何也没办法找到合法解,所以问题就转变成了,[l,r]区间内,dp[r]==dp[l-1] (mindp[i]) == dp[r],如果不成立-1,否则因为各个地方可以不矛盾的选择,所以只要选最大值-最小值就是答案 (maxdp[i])- (mindp[i]),区间定值查询最大最小值可以用st表,时间复杂度:O(nlogn)
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
ll s[N], dp[N], f[N], g[N][20], g1[N][20];
string str;
vector<int> vec;
void init()
{
for (int i = 1; i <= n; i++) g[i][0] = dp[i];
for (int j = 1; j <= 20; j++)
for (int i = 1; i <= n; i++)
if (i + (1 << j) - 1 <= n)
g[i][j] = max(g[i][j-1], g[i+(1<<(j-1))][j-1]);
for (int i = 1; i <= n; i++) g1[i][0] = dp[i];
for (int j = 1; j <= 20; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
g1[i][j] = min(g1[i][j-1], g1[i+(1<<(j-1))][j-1]);
}
ll querymax(int l, int r)
{
int len = log(r - l + 1) / log(2);
return max(g[l][len], g[r-(1<<len)+1][len]);
}
ll querymin(int l, int r)
{
int len = log(r - l + 1) / log(2);
return min(g1[l][len], g1[r-(1<<len)+1][len]);
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> s[i];
for (int i = 1; i <= n; i++) cin >> f[i];
for (int i = 1; i <= n; i++) dp[i] = dp[i-1] + f[i] - s[i];
init();
while (m--)
{
int a, b;
cin >> a >> b;
if (querymin(a,b) == dp[a-1] && dp[a-1] == dp[b])
cout << querymax(a,b) - querymin(a,b) << endl;
else puts("-1");
}
}
int main()
{
t = 1;
// cin >> t;
while(t--)
solve();
return 0;
}
F. Sports Betting
题意:给出n个人(n<=14),每个人的值为a[i],i与j打,i赢的概率为,不存在平局,输者淘汰制,一个人直接或间接打败其余所有人算胜者,问胜者的期望概率,取余1e9+7
分析:看的排行榜大佬的代码,思路巧妙。首先预处理第i个人打败第j个人的概率,有种状态,在状态i下,第j个人赢状态i下所有人的概率,然后开始计算,显然每个人都可能成为胜者,那就分别计算每个人i成为胜者的期望概率加起来就好了,初始化g[1<<i]=1,自己一个人肯定百分之百是胜者,然后枚举从小到大每种状态,如果状态i为i本身或者没有i本身,没意义跳过,跳过之后,我们计算所有失败情况的概率,一减就是胜利情况的概率了,这里枚举了状态j的所有情况,保留了其中所有包括i的状态,然后用最初1的概率循环乘dp[u][s],s这个人不在状态u内,在状态j内,并且打败了u状态每一人的概率(我最初想,如果是失败的概率的话,那么状态u被u以外的的任意一个s打败不都输了么,成功的概率不应该是所有符合条件的s的(1-dp[u][s]),怎么会是dp[u][s]呢,但事实上这里考虑了容斥,对于非全胜的情况可以向上转化,比如在算111101的时候,状态111000,这里有四种情况000000,000100,000001,000101,第一种失败的不算,第四种算,第二三种可以分别向上转化为不重复,111000+000100严格等价于111001+000100,所以这里只讨论跟状态u以外的全败即可,之前的状态均为已知)时间复杂度非常高n*,len(i)为二进制状态下1的个数,估算时间复杂度极高,过程中少量剪枝后,我走了遍n为14的数据,发现运行了141477994,接近1.5e8了,(题目时限4s)
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], dp[1 << N][N], f[N][N], g[1 << N];
string str;
vector<int> vec;
int ksm(int a, int b, int p)
{
int res = 1;
while (b)
{
if (b & 1) res = 1ll * res * a % p;
a = 1ll * a * a % p;
b >>= 1;
}
return res;
}
void solve()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> s[i];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
f[i][j] = 1ll * s[i] * ksm((s[i] + s[j]) % mod, mod - 2, mod) % mod;
for (int i = 0; i < (1 << n); i++)
for (int j = 0; j < n; j++)
{
dp[i][j] = 1;
for (int k = 0; k < n; k++)
if (i & (1 << k))
dp[i][j] = 1ll * dp[i][j] * f[j][k] % mod;
}
ll ans = 0;
for (int i = 0; i < n; i++)
{
memset(g, 0, sizeof g);
g[1 << i] = 1;
for (int j = 0; j < (1 << n); j++)
{
if (!(j & (1 << i))) continue;
if (j == (1 << i)) continue;
g[j] = 1;
for (int u = j; u; u = (u - 1) & j)
{
if (!(u & (1 << i))) continue;
if (u == j) continue;
ll cur = g[u];
for (int s = 0; s < n; s++)
if ((u ^ j) & (1 << s))
cur = cur * dp[u][s] % mod;
g[j] = (g[j] - cur) % mod;
}
}
ans += g[(1 << n) - 1];
ans %= mod;
}
if (ans < 0) ans += mod;
cout << ans << endl;
}
int main()
{
t = 1;
// cin >> t;
while(t--)
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!