CF Global Round 13 (1491) 笔记

Mar 22nd, 2021
  • 在其它设备中阅读本文章

第一次深夜打 CF 不回寝室.

A K-th Largest Value

给定 $a_{1,2,\ldots,n}\in\{0,1\}$ 数, 每次给定 $x$ 将 $a_x$ 变为 $1-a_x$ 或者询问第 $k$ 大.

直接记录有几个 $0/1$ 即可.

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

int a[100005];

int main() {
    int n,m,s=0,u,v; scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) {scanf("%d",&a[i]); s+=a[i];}
    while (m--) {
        scanf("%d%d",&u,&v);
        if (u==1) {a[v]?--s:++s; a[v]^=1;}
        else printf("%d\n",v<=s?1:0);
    }
    return 0;
}

B Minimal Cost

给定下图, 第 $i$ 行在第 $1\le a_i\le10^6$ 列处有一个障碍, 纵向移动一个障碍需要 $v$ 的代价, 横向需要 $v$ 的代价, 问最少需要多少代价使 $(1,0)$ 和 $(n,10^6+1)$ 连通.

考虑以下三种情况:

  • 如果相邻两行 $a_i$ 之差 $\ge2$, 则能直接从中间穿过去而不需要移动;
  • 否则如果之差 $=1$, 则可以将其中一个横向或纵向移动一次实现;
  • 否则需要横向移动任意一行, 然后变为第二种情况.
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;

int a[100005];

int main() {
    int t,n,u,v,p; scanf("%d",&t); while (t--) {
        scanf("%d%d%d",&n,&u,&v); p=v+min(u,v);
        for (int i=1;i<=n;++i) {
            scanf("%d",&a[i]);
            if (i>1&&a[i]!=a[i-1]) p=min(p,min(u,v));
            if (i>1&&abs(a[i]-a[i-1])>1) p=0;
        }
        printf("%d\n",p);
    }
    return 0;
}

C Pekora and Trampoline

有 $n$ 个排成一列的蹦床, 第 $i$ 个弹力系数为 $S_i$. 每次可以随意挑选一个蹦床 $p$ 出发, 然后跳到 $p+S_p$ 个蹦床, 直到 $p>n$ 为止, 同时跳到过 (没跳到不算) 的每个蹦床 $S_i\gets\max\{S_i-1,1\}$. 问最少几次能使所有 $S_i=1$.

直接暴力跳的过程中加一些优化即可.

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;

int s[5005],r[5005];

int main() {
    int t,n; ll a;
    scanf("%d",&t); while (t--) {
        scanf("%d",&n); a=0;
        for (int i=1;i<=n;++i) {
            scanf("%d",&s[i]);
            r[i]=i+1;
        }
        for (int i=1;i<=n;++i) {
            if (s[i]+i>=max(n,i+1)) {
                a+=s[i]+i-max(n,i+1);
                s[i]=max(n,i+1)-i;
            }
            while (s[i]>1) {
                for (int j=i;j<=n;) {
                    if (s[j]>1) j+=s[j]--;
                    else {
                        while (r[j]<=n&&s[r[j]]==1) r[j]=r[r[j]];
                        j=r[j];
                    }
                }
                ++a;
            }
        }
        printf("%lld\n",a);
    }
    return 0;
}

D Zookeeper and The Infinite Zoo

有一张无限个点的图, 有从 $u$ 到 $u+v$ 的有向边当且仅当 $u~\mathrm{and}~v=v$, 多次询问是否存在两点间道路.

有个结论, 不过直接看代码更好理解.

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;

int s[5005];
vector<int> e[5005];

int main() {
    int t,u,v,c;
    scanf("%d",&t); while (t--) {
        scanf("%d%d",&u,&v); c=0;
        if (u>v) {puts("NO"); goto query_end;}
        for (int i=0;i<30;++i)
            if ((c+=((u>>i)&1)-((v>>i)&1))<0) {
                puts("NO"); goto query_end;
            }
        puts("YES");
query_end:;
    }
    return 0;
}

E Fib-tree

结论是如果能拆分就拆分, 拆不动了说明不合法.

假设有两种拆分方式 $F_{a1}+F_{a2}=F_{b1}+F_{b2}$, 再下一步拆分一定会拆成相同的 $F_{c1}+F_{c2}+F_{c3}$.

F Magnets

G Switch and Flip

H Yuezheng Ling and Dynamic Tree

I Ruler Of The Zoo

不会, 都不会.

owo

mo-ha