• 10月8日考试 题解 (贪心+模拟+树链剖分+搜索)


    T1 小Z搭积木

    题目大意:小Z有$n$块积木。每个积木上面最多搭$a_i$块积木,积木可以摆很多列。问最少的列数。$nleq 5000$

    先把$a$排序,然后从上往下搭积木,看哪个积木没被用且$a$尽可能小。时间复杂度$O(n^2)$。

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    int n,a[5005],v[5005],ans,m;
    int main(){
        cin>>n;
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        for (int i=1;i<=n;i++)
        if(!v[i]){
            ans++;
            m=1;
            for (int j=i+1;j<=n;j++)
            if (!v[j]&&a[j]>=m){
                v[j]=1;
                m++;
            }
        }
        cout<<ans;
        return 0;
    }

    T2 动态仙人掌

    题目大意:数轴上有$n$个位置在$p_i$,高度为$h_i$的障碍。人可以向与正方向夹角45度的方向起跳,沿着与负方向夹角45度降落。一个障碍被越过当人在此位置时的高度大于等于障碍的高度。人可以在地面任意时刻起跳,在空中任意时刻降落,但是不能在空中起跳。问越过所有障碍最少跳多高。$nleq 3 imes 10^5$

    一开始想假了,距离正解只差一步之遥……

    显然对于每个障碍都有一个最晚的起跳点和最早的降落点。我们称其为一段区间。当两个区间有交集时,这段是不能降落的(因为再跳起达不到后一个障碍的高度)。所以我们不妨考虑区间合并,以$l$为关键字排序。当当前区间的$l$严格小于前面区间的$r$时合并区间,否则更新答案。

    时间复杂度$O(nlog n)$。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=300005;
    int n;
    struct node
    {
        int l,r;
    }a[N];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    bool cmp(node x,node y)
    {
        return x.l<y.l;
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++)
        {
            int p=read(),h=read();
            a[i].l=p-h,a[i].r=p+h;
        }
        sort(a+1,a+n+1,cmp);
        if (a[1].l<0){
            cout<<-1;
            return 0;
        }
        int nxt=a[1].r,pre=a[1].l;
        double ans=0;
        for (int i=2;i<=n;i++)
        {
            if (a[i].l<nxt){
                nxt=max(nxt,a[i].r);
            }else{
                ans=max(ans,(nxt-pre)*1.0/2);
                pre=a[i].l,nxt=a[i].r;
            }
        }
        ans=max(ans,(nxt-pre)*1.0/2);
        printf("%.1lf",ans);
        return 0;
    }

    T3 相交

    题目大意:有一个含有$n$个结点的树,$q$次询问。每次询问形如$(a,b,c,d)$,表示给$(a,b)$路径所有点打上标记,询问$(c,d)$路径上有没有被标记的点。询问完后$(a,b)$路径上的标记会消失。$n,qleq 10^5$

    傻逼题。直接上树剖就行了。

    代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=100005;
    int size[N],son[N],dep[N],fa[N],n,q;
    int dfn[N],top[N],tot;
    int head[N],cnt;
    int sum[N*4],lazy[N*4];
    struct node
    {
        int next,to;
    }edge[N*2];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void add(int from,int to)
    {
        edge[++cnt]=(node){head[from],to};
        head[from]=cnt;
    }
    inline void dfs_son(int now,int f)
    {
        size[now]=1;
        dep[now]=dep[f]+1;fa[now]=f;
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if (to==f) continue;
            dfs_son(to,now);
            size[now]+=size[to];
            if (size[to]>size[son[now]]) son[now]=to;
        }
    }
    inline void dfs_chain(int now,int topf)
    {
        dfn[now]=++tot;
        top[now]=topf;
        if (!son[now]) return;
        dfs_chain(son[now],topf);
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if(dfn[to]) continue;
            dfs_chain(to,to);
        }
    }
    inline void pushdown(int index,int l,int r)
    {
        int mid=(l+r)>>1,k=lazy[index];
        lazy[index]=0;
        sum[index*2]+=(mid-l+1)*k;
        sum[index*2+1]+=(r-mid)*k;
        lazy[index*2]+=k;lazy[index*2+1]+=k;
    }
    inline void update(int index,int l,int r,int ql,int qr,int k)
    {
        if (ql<=l&&r<=qr)
        {
            sum[index]+=(r-l+1)*k;
            lazy[index]+=k;
            return;
        }
        pushdown(index,l,r);
        int mid=(l+r)>>1;
        if(ql<=mid) update(index*2,l,mid,ql,qr,k);
        if(qr>mid) update(index*2+1,mid+1,r,ql,qr,k);
        sum[index]=sum[index*2]+sum[index*2+1];
    }
    inline int query(int index,int l,int r,int ql,int qr)
    {
        if (ql<=l&&r<=qr) return sum[index];
        pushdown(index,l,r);
        int mid=(l+r)>>1,res=0;
        if (ql<=mid) res+=query(index*2,l,mid,ql,qr);
        if (qr>mid) res+=query(index*2+1,mid+1,r,ql,qr);
        return res;
    }
    inline void updrange(int x,int y,int k)
    {
        while(top[x]!=top[y])
        {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            update(1,1,n,dfn[top[x]],dfn[x],k);
            x=fa[top[x]];
        }
        if (dep[x]>dep[y]) swap(x,y);
        update(1,1,n,dfn[x],dfn[y],k);
    }
    inline int qrange(int x,int y)
    {
        int res=0;
        while(top[x]!=top[y])
        {
            if (dep[top[x]]<dep[top[y]]) swap(x,y);
            res=(res+query(1,1,n,dfn[top[x]],dfn[x]));
            x=fa[top[x]];
        }
        if (dep[x]>dep[y]) swap(x,y);
        res=(res+query(1,1,n,dfn[x],dfn[y]));
        return res;
    }
    int main()
    {
        n=read();
        for (int i=1;i<n;i++)
        {
            int x=read(),y=read();
            add(x,y);add(y,x);
        }
        dfs_son(1,0);
        dfs_chain(1,1);
        q=read();
        while(q--)
        {
            int a=read(),b=read(),c=read(),d=read();
            updrange(a,b,1);
            int tmp=qrange(c,d);
            if (tmp>0) printf("YES
    ");
            else printf("NO
    ");
            updrange(a,b,-1);
        }
        return 0;
    }

    T4 聪明格

    题目大意:给定一个$n imes n$的棋盘,现要求往里面填$1-n$的数,使得每一行每一列没有重复的数字。同时给定一个$n imes n$的图,相同的数字构成一个联通块,表示棋盘内对应连通块内数字的积要等于图中的数。问有几种方案,并输出字典序最小的方案数。

    爆搜+剪枝最多70pts.正解的搜索策略很妙。

    考虑到对于连通块的限制比对于行和列的限制要严格的多,所以我们不妨考虑这样一种策略:先把连通块填满,然后判断是否合法。接下来是剪枝:我们将连通块按照大小排序,如果大小相同按照因数个数排序(这样使选择的余地尽可能小),然后将每个连通块填满即可。

    时间复杂度$O(metaphysics)$。

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N=11,M=505;
    const int dx[]={0,1,-1,0,0};
    const int dy[]={0,0,0,1,-1};
    struct Node{
        int c[N][N];
    }ans[M];
    struct node{
        int x,y;
    };
    inline void debug(){
        puts("fuck!");
    }
    bool operator < (const Node x,const Node y)
    {
        for (int i=1;i<N;i++)
            for (int j=1;j<N;j++)
                if (x.c[i][j]!=y.c[i][j])
                    return x.c[i][j]<y.c[i][j];
        return 0;
    }
    vector<node> v[M];
    vector<int> p[M];
    int a[N][N],l[N][N],h[N][N],vis[N][N],c[N][N],n,cnt,tot;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline int factor(int x)
    {
        int t=0;
        for (int i=1;i*i<=x;i++)
            t+=(int)(x%i==0);
        return t;
    }
    bool cmp(vector<node> x,vector<node> y)
    {
        if (x.size()==y.size()) 
            return factor(x[0].x)<factor(y[0].x);
        return x.size()<y.size();
    }
    inline void split(int t,int x)
    {
        for (int i=1;i<=n;i++)
            if (x%i==0) p[t].push_back(i);
    }
    inline bool judge(int x,int y)
    {
        if (x<1||x>n||y<1||y>n||vis[x][y]) return 0;
        return 1;
    }
    inline void dfs(int x,int y)
    {
        vis[x][y]=1;
        v[cnt].push_back((node){x,y});
        for (int i=1;i<=4;i++)
        {
            int xx=x+dx[i],yy=y+dy[i];
            if (judge(xx,yy)&&a[xx][yy]==a[x][y])
                dfs(xx,yy);
        }
    }
    void work(int dep);
    inline void fill(int t,int dep,int s,int tot)
    {
        if (dep>tot) work(t+1);
        else
        {
            int x=v[t][dep].x,y=v[t][dep].y;
            for (int i=0;i<p[t].size();i++)
            {
                int u=p[t][i];
                if ((s==u||dep<tot)&&s%u==0&&!h[x][u]&&!l[y][u])
                {
                    h[x][u]=l[y][u]=1;
                    c[x][y]=u;
                    fill(t,dep+1,s/u,tot);
                    c[x][y]=0;
                    h[x][u]=l[y][u]=0;
                }
            }
        }
    }
    inline void work(int dep)
    {
        if (dep>cnt){
            tot++;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=n;j++)
                    ans[tot].c[i][j]=c[i][j];
        }
        else{
            int num=v[dep][0].x,tot=v[dep].size()-1;
            fill(dep,1,num,tot);
        }
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                a[i][j]=read();
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                if (vis[i][j]) continue;
                cnt++;v[cnt].push_back((node){a[i][j],0});
                dfs(i,j);
            }
        sort(v+1,v+cnt+1,cmp);
        for (int i=1;i<=cnt;i++)
            split(i,v[i][0].x);
        work(1);
        printf("%d
    ",tot);
        Node t=ans[1];
        for (int i=2;i<=tot;i++)
            t=min(t,ans[i]);
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++)
                printf("%d ",t.c[i][j]);
            printf("
    ");
        }
        return 0;
    }
  • 相关阅读:
    BUAA_OO_2020_Unit3 Summary
    BUAA_OO_2020_Unit2 Summary
    DataFrame的遍历
    ESMM提升CVR的论文summary
    FaceBook 关于提升CTR的论文研究
    OO终章·GRAND BATTLE
    第三单元规格作业博客总结
    OO电梯单元作业总结
    【OO多项式求导作业总结】
    提问回顾与个人总结
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13781560.html
Copyright © 2020-2023  润新知