A. Robot Program
题意:
一个机器人要从((0,0))位置走到((x,y))位置,每次可以进行5种操作,分别是向上下左右方向走一格,或者是待在原地。机器人不能连续进行同样的两次操作(例如不能连续向右走两次,但是可以走一次,停在原地一次,然后再走一次),问最少进行多少次操作可以到达目的地
思路:
当(x=y)或者x与y的差只有1时,可以一直走,不需要停,否则必须要在中途停下来。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t, x, y;
int main(){
cin>>t;
while(t--){
cin >> x >> y;
if(abs(x-y)<=1){
cout << x + y << endl;
}
else{
cout << min(x, y) * 2 + 1 + 2 * (max(x, y) - (min(x, y) + 1)) << endl;
}
}
return 0;
}
B. Toy Blocks
题意:
有 n 个盒子,每个盒子有 (a_i) 个糖果。现在你可以向一些盒子中添加糖果,使得任选一个盒子,将其中的糖果分到其余 n-1 个盒子中,每个盒子的糖果数量相等。问最少需要添加多少糖。
思路
直接暴力做,对于每个点,判断一下把其他糖果填平的糖果即可,这里的填平是需要将其他的糖果补充到他们的最大值,然后再看剩下的糖果数量,判断是否还需要再填一层
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int t, n, x, a[N];
long long anss;
long long sum,maxn=-1,minn=0x3f3f3f3f;
int main(){
cin >> t;
while(t--){
cin >> n;
sum = 0;
anss = 0;
for (int i = 0; i < n;i++){
cin >>a[i];
sum += a[i];
}
sort(a, a + n);
maxn = a[n - 1];
minn = a[0];
for (int i = 0; i < n;i++){
minn = a[i];
if(i==n-1){
maxn = a[i - 1];
}
if (maxn * (n - 1) - (sum - minn) > minn)
{
anss=max(anss, maxn * (n - 1) - (sum - minn) - minn );
}
else if (maxn * (n - 1) - (sum - minn) > minn)
{
anss = max(anss, (long long)0);
}
else{
if(((minn-(maxn*(n-1)-(sum-minn)))%(n-1)==0)){
anss = max(anss, (long long)0);
}
else{
anss = max(anss, (n - 1) - (minn - (maxn * (n - 1) - (sum - minn))) % (n - 1));
}
}
}
cout << anss << endl;
}
return 0;
}
C. Two Brackets
题意:
计算有多少配对的括号
思路:
记录一下前面出现的左括号lsum,当遇到右括号时,如果lsum大于0,就将lsum--,然后答案++
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t,lnum1,lnum2,sum;
string s;
int main(){
cin>>t;
while(t--){
cin >> s;
sum = 0;
lnum1 = 0;
lnum2 = 0;
for (int i = 0;i<s.size();i++){
if(s[i]=='('){
lnum1++;
}
else if(s[i]==')'){
if(lnum1){
lnum1--;
sum++;
}
}
else if(s[i]=='['){
lnum2++;
}
else if(s[i]==']'){
if(lnum2){
lnum2--;
sum++;
}
}
}
cout << sum << endl;
}
return 0;
}
D. Radio Towers
题意:
一共0到n+1共n+2个点,每个点上都可以放置或者不放置一个信号塔,信号塔的辐射范围是1-n,每个点只能被一个信号塔覆盖,0号和n+1号点不能被覆盖,问符合条件的放置方法占全部的多少
思路:
找规律即可,分子为斐波那契数列,分母为(2^n)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int mod = 998244353;
typedef long long LL;
LL n,fz,fm,fib[N];
LL get_fz(LL n){
fib[1] = 1;
fib[2] = 1;
for (int i = 2; i <= n;i++){
fib[i] = ((LL)fib[i - 1] + (LL)fib[i - 2]) % mod;
}
return fib[n];
}
LL qmi(LL a, LL k, LL p) {
LL res = 1 % p; // res记录答案, 模上p是为了防止k为0,p为1的特殊情况
while(k) { // 只要还有剩下位数
if (k & 1) res = (LL)res * a % p; // 判断最后一位是否为1,如果为1就乘上a,模上p, 乘法时可能爆int,所以变成long long
k >>= 1; // 右移一位
a = (LL) a * a % p; // 当前a等于上一次的a平方,取模,平方时可能爆int,所以变成long long
}
return res;
}
LL get_inv(LL a, LL p) {
return a % p == 0? -1: qmi(a, p - 2, p);
}
LL get_fm(LL n){
LL temp = qmi(2,n,mod);
return get_inv(temp, mod);
}
int main(){
cin>>n;
fz = get_fz(n);
fm = get_fm(n);
cout << fz * fm % mod << endl;
return 0;
}
E. Two Editorials
大意:
一共有m个位于[1,n]的区间p,现在有长度为K的区间b和c。设对于区间p[i],定义a[i]为p[i]分别与b,c相交长度的较大值,现在问区间b和c位于何处时,a[i]的和最大
思路:
纯暴力肯定不行,所以需要考虑对其进行优化,因为对于区间p,如果它的中点在b和c的中点的左侧,那么一定是对b产生贡献,反之则是对c产生贡献,那么对于每个点i,p个区间都可以分为两部分,前一部分对b产生贡献,后一部分对c产生贡献,所以首先对m个p区间按照中点排序,然后遍历n,找到对于每个点i的分界点,即(pre[i])代表有多少区间的中点在i的左侧。然后跑一遍前缀和,(sum[i][j])代表对于位置i,前j个区间可以产生的贡献,最后暴力枚举区间b和c,算一下两个区间获得的贡献即可。这样复杂度为(O(n*m))
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
int n, m, k,pre[N],sum[N][N];
struct node
{
int l, r;
}a[N];
bool cmp(node a,node b){
return (a.l + a.r < b.l + b.r);
}
int main(){
cin>>n>>m>>k;
for (int i = 0; i < m;i++){
cin >> a[i].l >> a[i].r;
}
sort(a, a + m, cmp);
int j = 0;
for (int i = 1; i <= n;i++){
while(j<m&&(a[j].l+a[j].r<=2*i)){
j++;
}
pre[i] = j-1;
}
for (int i = 1; i <= n - k + 1;i++){
int l = i, r = i + k - 1; //当前区间的左右范围
for (int j = 1; j <= m;j++){
sum[i][j] = sum[i][j - 1] + max(0, min(a[j - 1].r, r) - max(a[j - 1].l,l )+1);
}
}
int res = 0;
for (int i = 1; i <= n - k + 1;i++){
for (int j = i; j <= n - k + 1;j++){
int mid = (i + j + k - 1) >> 1;
res = max(res, sum[i][pre[mid] + 1] + sum[j][m] - sum[j][pre[mid] + 1]);
}
}
cout << res << endl;
return 0;
}
F. Divide Powers
题意:
给出一个数组(cnt_i)代表当前有(cnt_i)个(2^i),当(i>0)时,可以将一个(2^i)变成两个(2^{i-1}),现在又两个操作,一类是修改(cnt_i),另一类是询问进行多少次修改后会有不少于(k)个元素小于等于(2^x)
思路:
对于大于(2^x)的元素,将其修改到(2^x),可以比修改小于(2^x)的元素要多出一个,所以优先修改大于(2^x)的元素,但是如果修改到(2^x)获得的元素比需要的多,那么需要判断是否修改小于(2^x)的元素,也就是需要记录当前已经选择的元素,让他们全部修改为(2^0)可以获得的元素个数。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
LL n, q, cnt[35],op,a[35];
int main(){
cin >> n >> q;
a[0] = 1;
for (int i = 0; i < n;i++){
cin >> cnt[i];
if(i){
a[i] = a[i - 1] * 2;
}
}
while(q--){
cin >> op;
if(op==1){
LL pos, val;
cin >> pos >> val;
cnt[pos] = val;
}
else{
LL x, k,sum=0,res=0;
cin >> x >> k;
for (int i = 0; i <= x;i++){
k -= cnt[i];
sum += (a[i]-1) * cnt[i]; //记录当前选择的还可以多产生多少符合条件的值
}
if(k<=0){
cout << 0 << endl;
continue;
}
for (int i = x + 1; i < n;i++){
if((a[i-x]*cnt[i])<=k){
k -= a[i - x] * cnt[i];
res += (a[i - x] - 1) * cnt[i];
sum += (a[i] - a[i - x]) * cnt[i];
}
else{
int p=k/a[i-x];
k%=a[i-x];
sum+=p*(a[i]-a[i-x]);
res+=p*(a[i-x]-1);
for (int j=i;j>=x;j--)
{
if (k<=sum)
{
res+=k;
k=0;
break;
}
if (a[j-x-1]<=k)
{
k-=a[j-x-1];
sum+=a[j-1]-a[j-x-1];
res+=a[j-x-1];
}
else res++;
}
break;
}
}
if(k<=sum){
cout << res + k << endl;
}
else
cout << -1 << endl;
}
}
return 0;
}
G. Game On Tree
大意:
Alice 和 Bob 在玩一个游戏。他们有一棵由 n 个结点组成的树。一开始,Bob 有 k个卡片,其中第i个卡片位于结点 (a_i),在游戏开始之前,Alice 将在这棵树的一个结点上放置一个卡片。
这个游戏由一些回合组成。每个回合都将有以下事件发生(完全按照以下顺序):
- Alice 可以把她的卡片移到相邻的结点,或者不移动;
- 对于 Bob 的每一张卡片,他可以把这张卡片移到相邻的结点,或者不移动。注意:每个卡片的选择都是独立的。
当 Alice 的卡片与 Bob 的任意一张(或多张)卡片在同一结点时,游戏结束。(Bob 自己的多张卡片可以置于同一结点上,即使它们的初始位置一定是不同的)。
Alice 希望游戏回合越多越好,Bob则相反。如果某回合中间游戏结束(即 Alice 把卡片移到了有 Bob 卡片的结点上),这回合依然算入总回合数。
对于每个结点,计算 Alice 一开始将卡片放在该结点时游戏将持续的回合数。
思路:
首先利用拓扑排序求出来每个点距离Alice的最近的一个卡片的距离(dis[i]),对于Bob来说,必然是想前往(dis[x])较大的点,而对于Alice来说,必然是所有的点都向Bob逼近,得到(dis[x])数组后,可以从最大的值开始更新周围的点的答案,但是这样复杂度会很高,所以引入(h[i])数组,代表上一次利用i点更新周围点的答案时,i点距离最近的一个“敌人”的距离,所以只有当当前距离最近的"敌人"的距离大于(h[i])时,才利用这个点更新答案,否则不需要更新。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m,res[N],dis[N],maxn,h[N];
vector<int> mp[N],D[N];
queue<int>q;
int main(){
cin >> n;
for (int i = 1; i < n;i++){
int u,v;
cin >> u >> v;
mp[u].push_back(v);
mp[v].push_back(u);
dis[i] = -1;
}
dis[n] = -1;
cin >> m;
while(m--){
int x;
cin >> x;
dis[x] = 0;
q.push(x);
}
while(!q.empty()){
int u = q.front();
q.pop();
for (int i = 0; i < mp[u].size();i++){
int v = mp[u][i];
if(dis[v]>=0)
continue;
dis[v] = dis[u] + 1;
q.push(v);
}
D[dis[u]].push_back(u);
maxn = max(maxn, dis[u]);
}
queue<pair<int, int>> q1;
for (int i = maxn; i > 0;i--){
for (int j = 0; j < D[i].size();j++){
int u = D[i][j];
if(h[u]<dis[u]){
if(res[u]==0){
res[u] = i;
}
h[u] = dis[u];
q1.push({dis[u],u});
}
while(!q1.empty()){
int d = q1.front().first;
int u = q1.front().second;
q1.pop();
if(--d==0)
continue; //更新过来直接是敌人
for(int j = 0;j<mp[u].size();j++){
int v = mp[u][j];
int temp = min(d, dis[v]);
if(temp>h[v]){
if(res[v]==0)
res[v] = i;
h[v] = temp;
q1.push({temp, v});
}
}
}
}
}
for (int i = 1; i <= n;i++){
cout << res[i] << ' ';
}
cout << endl;
return 0;
}