abc318

abc318

总评:智障了好多次,不过这种智障的修改感觉很有意义,因为平常考试我也会扔掉简单的去写麻烦的

D

发现 \(n\le 16\) 因为我们最多选 \(n/2\) 条边,所以只会最多选出来 \(8\) 条边,直接 dfs 找每个点选哪条边即可

要注意可以不选某条边

Code

E

首先考虑对于两个颜色相同的点 \(i,j\) ,他们会产生的贡献是 \(i,j\) 之间的距离减去中间和 \(i,j\) 一样颜色的个数

\(v_i\) 表示某个颜色的第 \(i\) 个所在原序列的位置,那么 \(i,j\) 的贡献就是 \((v_j-v_i-1)-(j-i-1)\)

发现可以把 \(i\)\(j\) 的贡献拆开,那也就是 \(v_j-j+i-v_i\)

也就是说 \(i\) 后面的每一个 \(j\) 都能给 \(i\) 提供 \(i-v_i\) 的贡献,\(j\) 前面的每一个 \(i\) 都可以给 \(j\) 提供 \(v_j-j\) 的贡献

用个 \(vector\) 记录每个颜色所在的位置即可

Code

F

题意:

在一条数线上有一个章鱼形机器人和 \(N\)个宝物。第 \(i\) 个宝物 \((1\leq i\leq N)\) 位于坐标 \(X_i\) 处。
机器人有一个头和\(N\) 条腿, \(i\) 条腿 \((1\leq i\leq N)\) 的长度为\(L_i\)

求机器人能抓住所有 \(N\) 个宝物的整数 \(k\) 的个数如下。

  • 将头部置于坐标 \(k\) 处。
  • 依次对 \(i=1,2,\ldots,N\) 重复以下步骤:如果在距离头部 \(L_i\)的距离内,即在满足 \(k-L_i\leq x\leq k+L_i\) 的坐标 \(x\)处,有一件宝物尚未被抓取,则选择其中一件宝物并抓取。

分析:

先考虑如果固定 \(k\) 怎么判断,直接贪心的从大到小排序 到 \(k\) 的距离和腿的距离,让其一一匹配即可

可以发现一个性质,如果 \(k\) 出现在 \([l,r]\) 中,前提是其中没有任何一条腿正好碰到某一个宝藏,(正好碰到的意思是这个宝藏到 \(k\) 的距离等于腿的长度) ,那么对于任意的 \(i,j\in[l,r]\) ,如果 \(i\) 作为 \(k\) 合法,那么 \(j\) 作为 \(k\) 也合法

接下来证明这个结论:如果 \(i\) 能触摸到宝藏 \(j\) ,那么不管 \(k\)\([l,r]\) 的任意一个位置,\(i\) 都能触摸到 \(j\) (其实这个结论是可以打表打出来的/cf)

考虑反证:如果一个点合法,说明每一条腿的长度至少比和他匹配的宝藏的距离大于 \(1\) ,那么如果出现 \(i\) 不能摸到 \(j\) ,那么说明在左右移动的过程中,有一个 \(i\) 他摸不到宝藏了,从能到不能,那么一定有一个边界导致 \(i\) 刚好摸到 \(j\) ,与前提相反,所以不会出现这种情况

接下来直接钦定某一个宝藏是由那一条腿碰到的,这样会有 \(O(n^2)\)\(k\) ,把他们分成了 \(O(n^2)\) 段区间

只要在每一段随便选一个数判断合不合法即可,复杂度 \(O(n^3logn)\) 可过

代码:

int n,x[N],l[N];
int a[N],v[N],cnt;

bool check(int k){
    FOR(i,1,n) a[i]=abs(k-x[i]);
    sort(a+1,a+n+1);
    FOR(i,1,n){
        if (a[i]>l[i]) return 0;
    }
    return 1;
}

signed main(){
    scanf("%lld",&n);
    FOR(i,1,n) scanf("%lld",&x[i]);
    FOR(i,1,n) scanf("%lld",&l[i]);
    FOR(i,1,n){
        FOR(j,1,n){
            int k=x[i]+l[j];
            v[++cnt]=k;
            k=x[i]-l[j];
            v[++cnt]=k;
        }
    }
    sort(v+1,v+cnt+1);sort(l+1,l+n+1);
    cnt=unique(v+1,v+cnt+1)-v-1;
    int ans=0;
    FOR(i,1,cnt-1){
        if (check(v[i]+1)) ans+=(v[i+1]-v[i]-1);
    }
    FOR(i,1,cnt) if (check(v[i])) ans++;
    printf("%lld\n",ans);
    return 0;
}

G

原题链接

题目分析:

判断路径是否经过某个点,还是在图上的操作,显然想到圆方树。

直接建出圆方树,并在 \(A\)\(C\) 路径上的圆点和方点连接的圆点中,若有 \(B\) 那就 \(Yes\)

代码:

bool flag;
int n,m,A,B,C;
int fa[N],dep[N];
int dfn[N],low[N],cnt,stk[N],top,tot;
vector<int> G1[N],G2[N];

void add(int u,int v){
    G2[u].p_b(v);
    G2[v].p_b(u);
}

void tarjan(int u){
    dfn[u]=low[u]=++cnt;
    stk[++top]=u;
    for (auto v:G1[u]){
        if (!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]){
                int y;add(++tot,u);
                do{
                    y=stk[top--],add(tot,y);
                }while(y!=v);
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}

void dfs(int u,int fath){
    fa[u]=fath;dep[u]=dep[fath]+1;
    for (auto v:G2[u]){
        if (v==fath) continue;
        dfs(v,u);
    }
}

void check(int u){
    if (u<=n&&u==B) flag=1;
    else{
        for (auto v:G2[u]){
            if (v==B) flag=1;
        }
    }
}

signed main(){
    scanf("%d%d",&n,&m);tot=n;
    scanf("%d%d%d",&A,&B,&C);
    for (int i=1;i<=m;i++){
        int u=read(),v=read();
        G1[u].p_b(v);
        G1[v].p_b(u);
    }
    tarjan(1);dfs(1,0);flag=0;
    while(A!=C){
        if (dep[A]<dep[C]) swap(A,C);
        check(A);
        A=fa[A];
    }
    check(A);
    if (flag) puts("Yes");
    else puts("No");
    return 0;
}
posted @ 2023-09-04 09:39  taozhiming  阅读(99)  评论(1编辑  收藏  举报