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

T1 小Z搭积木

题目大意:小Z有$n$块积木。每个积木上面最多搭$a_i$块积木,积木可以摆很多列。问最少的列数。$n\leq 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度降落。一个障碍被越过当人在此位置时的高度大于等于障碍的高度。人可以在地面任意时刻起跳,在空中任意时刻降落,但是不能在空中起跳。问越过所有障碍最少跳多高。$n\leq 3 \times 10^5$

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

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

时间复杂度$O(n\log 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,q\leq 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\n");
        else printf("NO\n");
        updrange(a,b,-1);
    }
    return 0;
}

T4 聪明格

题目大意:给定一个$n \times n$的棋盘,现要求往里面填$1-n$的数,使得每一行每一列没有重复的数字。同时给定一个$n \times 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\n",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("\n");
    }
    return 0;
}
posted @ 2020-10-08 15:35  我亦如此向往  阅读(158)  评论(0编辑  收藏  举报