zoukankan      html  css  js  c++  java
  • 2021.10.19 CSP 模拟赛 总结

    T1

    题意: (n) 个人摘苹果,跳起高度为 (a_i),苹果高度为 (h_i),高度小的先摘,摘了就没了

    直接排序+双指针,复杂度 (O(n+m))

    T2

    题意:要轰炸一个有向图的所有点,如果存在两个不同点 (i)(j) ,并且 (i)(j) 连通

    那么 (i)(j) 不能在同一次轰炸,问最少要多少次轰炸

    tarjan 缩点 + 找最长路。考试时没有想到要最长路,缩点不是很会处理重边

    复杂度 (O(n+m))

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int N=1000005;
    int n,m,sz[N],ans,rd[N],f[N],q[N],hd,tl;
    int low[N],dfn[N],clk,s[N],top,cl[N],tot;
    int lst1[N],nxt1[N],to1[N],cnt1;
    int lst2[N],nxt2[N],to2[N],cnt2;
    inline void Ae1(int fr,int go) { to1[++cnt1]=go,nxt1[cnt1]=lst1[fr],lst1[fr]=cnt1; }
    inline void Ae2(int fr,int go) { to2[++cnt2]=go,nxt2[cnt2]=lst2[fr],lst2[fr]=cnt2; }
    void tarjan(int u) {
        dfn[u]=low[u]=++clk,s[++top]=u;
        for(int i=lst1[u],v;i;i=nxt1[i]) {
            if(!dfn[v=to1[i]]) {
                tarjan(v);
                low[u]=min(low[u],low[v]);
            } else if(!cl[v]) {
                low[u]=min(low[u],dfn[v]);
            }
        }
        if(low[u]==dfn[u]) {
            ++tot,sz[tot]=1;
            while(s[top]!=u)
                ++sz[tot],cl[s[top]]=tot,--top;
            cl[u]=tot,--top;
        }
    }
    int main() {
        // freopen("bomb.in","r",stdin);
        // freopen("bomb.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1,u,v;i<=m;i++) {
            scanf("%d%d",&u,&v);
            Ae1(u,v);
        }
        for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
        for(int i=1;i<=n;i++)
            for(int j=lst1[i];j;j=nxt1[j])
                if(cl[i]!=cl[to1[j]])
                    Ae2(cl[i],cl[to1[j]]),++rd[cl[to1[j]]];
        for(int i=1;i<=tot;i++)if(!rd[i])q[++tl]=i,f[i]=sz[i];
        for(int u;hd<tl;) {
            u=q[++hd],ans=max(ans,f[u]);
            for(int i=lst2[u],v;i;i=nxt2[i]) {
                --rd[v=to2[i]];
                f[v]=max(f[v],f[u]+sz[v]);
                if(!rd[v])q[++tl]=v;
            }
        }
        printf("%d",ans);
    }
    

    T3

    题意:一个无向图,一条路径长度是所有边权的最大值,两点距离是两点路径长度的最小值

    操作 1 是加边,操作 2 是给 (i,j,p,q) ,算出 (d1=dis(i,j),d2=dis(p,q)) ,取出个数为 (d1,d2) 的两堆石子玩 ( ext{NIM}) 博弈

    然后问每次操作 2 谁赢

    显然的 Kruskal + LCA ,查询距离解决

    博弈的话直接异或即可

    操作 1 个数 (le5000)

    所以直接暴力加边,重跑一遍生成树即可

    复杂度 (O(5000n+Tlog n))

    (nle5000) ,卡过

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int N=5005,M=105005;
    int n,m,T,ff[N],lst[N],nxt[N<<1],to[N<<1],cnt,dep[N],fa[N][15];
    LL qz[N<<1],mx[N][15],Ra,Rb;
    char op[5];
    struct Ed { int u,v; LL w; }e[M];
    inline bool cmp(Ed A,Ed B) { return A.w<B.w; }
    int fd(int x) { return ff[x]==x?x:ff[x]=fd(ff[x]); }
    inline void Ae(int fr,int go,LL vl) {
        to[++cnt]=go,qz[cnt]=vl;
        nxt[cnt]=lst[fr],lst[fr]=cnt;
    }
    void dfs(int u,int f) {
        dep[u]=dep[f]+1,fa[u][0]=f;
        for(int i=1;i<=13;i++) {
            fa[u][i]=fa[fa[u][i-1]][i-1];
            mx[u][i]=max(mx[u][i-1],mx[fa[u][i-1]][i-1]);
        }
        for(int i=lst[u],v;i;i=nxt[i])
            if((v=to[i])^f)
                mx[v][0]=qz[i],dfs(v,u);
    }
    inline void kru() {
        cnt=1;
        for(int i=1;i<=n;i++)
            lst[i]=0,ff[i]=i,dep[i]=0;
        for(int i=1,tt=0,p,q;i<=m;i++) {
            p=fd(e[i].u),q=fd(e[i].v);
            if(p==q)continue;
            ff[q]=p,++tt;
            Ae(e[i].u,e[i].v,e[i].w);
            Ae(e[i].v,e[i].u,e[i].w);
            if(tt==n-1)break;
        }
        dfs(1,1);
    }
    inline LL LCA(int x,int y) {
        if(dep[x]<dep[y])x^=y^=x^=y;
        register LL res=0;
        for(int i=13;~i;i--)
            if(dep[fa[x][i]]>=dep[y])
                res=max(res,mx[x][i]),x=fa[x][i];
        if(x==y)return res;
        for(int i=13;~i;i--)
            if(fa[x][i]!=fa[y][i]) {
                res=max(res,max(mx[x][i],mx[y][i]));
                x=fa[x][i],y=fa[y][i];
            }
        return max(res,max(mx[x][0],mx[y][0]));
    }
    int main() {
        // freopen("game.in","r",stdin);
        // freopen("game.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) {
            scanf("%d%d%lld",&e[i].u,&e[i].v,&e[i].w);
        }
        sort(e+1,e+m+1,cmp);
        kru();
        scanf("%d",&T);
        for(int a,b,c,d;T--;) {
            scanf("%s",op);
            if(op[0]=='a') {
                ++m;
                scanf("%d%d%lld",&e[m].u,&e[m].v,&e[m].w);
                for(int i=m;i>1;i--) {
                    if(cmp(e[i],e[i-1]))
                        swap(e[i],e[i-1]);
                    else break;
                }
                kru();
            } else {
                scanf("%d%d%d%d",&a,&b,&c,&d);
                Ra=LCA(a,b);
                Rb=LCA(c,d);
                // printf("%lld %lld
    ",Ra,Rb);
                if(Ra!=Rb)puts("madoka");
                else puts("Baozika");
            }
        }
    }
    

    T4

    题意:一个序列的美观度 = 序列的数的和 / 序列被划分成的极长单调区间的个数(第一个区间必须单调递增)

    求一个序列的所有子序列中美观度的最大值

    重要性质:所选要么是一个单调上升序列,要么是一个上升序列和一个下降序列

    因为这两个如果选出最大值,其他都是小的,只会让答案减小

    就正着、反着分别求一次最大递增序列,然后取最大值即可

    求的话直接离散化+树状数组

    #include<bits/stdc++.h>
    using namespace std;
    typedef long double LD;
    typedef long long LL;
    typedef double db;
    const int N=100005;
    int n,m;
    LL x[N],y[N],f[N],g[N],t[N];
    db ans;
    inline void mdy(int p,LL v) {
        for(;p<=m;p+=p&-p)t[p]=max(t[p],v);
    }
    inline LL ask(int p) {
        register LL res=0;
        for(;p;p-=p&-p)res=max(res,t[p]);
        return res;
    }
    int main() {
        // freopen("seq.in","r",stdin);
        // freopen("seq.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&x[i]),y[i]=x[i];
        sort(y+1,y+n+1);
        m=unique(y+1,y+n+1)-y-1;
        y[++m]=1e9;
        for(int i=1,v;i<=n;i++) {
            v=lower_bound(y+1,y+m+1,x[i])-y;
            f[i]=ask(v-1)+x[i];
            mdy(v,f[i]);
        }
        memset(t,0,sizeof(t));
        for(int i=n,v;i;i--) {
            v=lower_bound(y+1,y+m+1,x[i])-y;
            g[i]=ask(v-1)+x[i];
            mdy(v,g[i]);
        }
        for(int i=1;i<=n;i++) {
            ans=max(ans,1.0*f[i]);
            ans=max(ans,1.0*(f[i]+g[i]-x[i])/2.0);
        }
        printf("%.3f",ans);
    }
    

    总结

    • T1:**
    • T2:第一次打 tarjan 缩点,以后要多角度思考缩完后的 DAG 如何求答案
    • T3:告诉我们相信暴力能骗分
    • T4:考虑答案最优性要满足什么条件
  • 相关阅读:
    052-PHP输出多个参数
    045-利用反射机制,简单的实现PHP插件模式
    044-PHP获得多个类对应的反射信息
    043-PHP简单获得一个类对应的反射信息
    042-PHP使用闭包函数递归无限级分类
    040-PHP使用闭包函数来进行父实例的变量自增,正确示例
    039-PHP使用闭包函数来进行父实例的变量自增,错误示例
    038-PHP向返回的闭包函数实例中,传递外部变量参数
    python——基本数据类型1——简介
    java知识整理
  • 原文地址:https://www.cnblogs.com/KonjakLAF/p/15430922.html
Copyright © 2011-2022 走看看