zoukankan      html  css  js  c++  java
  • 11月3日考试 题解 (背包+单调栈+树链剖分+贪心)

    写错两个freopen,230->30……

    T1 软件

    原题:洛谷P1800

    DP方程不难想到。设$f_{i,j}$表示前$i$个人做了第一个软件的$j$个模块的情况下最多能做多少第二个软件模块。发现直接转移复杂度太高,考虑二分答案。于是就变成了可行性问题,每次只需看在规定天数下是否能完成任务即可。

    时间复杂度$O(n^3log n)$。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N=105;
    int f[N][N],a[N],b[N],n,m;
    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 bool check(int x)
    {
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for (int i=1;i<=n;i++)
            for (int j=0;j<=m;j++)
                for (int k=j;k>=0;k--)
                {
                    int now=x-a[i]*(j-k);
                    if (now<0) break;
                    if (f[i-1][k]>0) f[i][j]=max(f[i][j],f[i-1][k]+now/b[i]);
                }
        return f[n][m]-1>=m;
    }
    int main()
    {
        n=read();m=read();
        for (int i=1;i<=n;i++)
            a[i]=read(),b[i]=read();
        int l=0,r=20000,ans;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if (check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d",ans);
        return 0;
    }

    T2 最大后缀值个数

    题目大意:一棵根节点为$1$的树,点有点权,求出根节点到每个点的有向链上的后缀最大值的个数。后缀最大值指从目标节点开始到此节点其值为路径上最大值。

    不难想到单调栈。但关键点在于回溯。

    我们可以在单调栈上二分出它插入的节点,然后将此时栈的大小和此位置的元素用临时变量保存下来,将当前权值插入栈中,并改变栈大小;等到回溯的时候再用临时变量回溯即可。

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

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int n;
    int val[N],f[N],head[N],tnt=0;
    struct edge{
        int link,v;
    }q[N<<1];
    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;
    }
    void put(int x,int y){
        q[++tnt].v=y;
        q[tnt].link=head[x];
        head[x]=tnt;
    }
    int ans[N],st[N],cnt;
    int find(int l,int r,int v){
         while(l<r){
             int mid=(l+r)>>1;
             if(st[mid]>=v){
                 l=mid+1;
             }
             else r=mid;
         }
         return l;
    }
    void dfs(int s,int fa){
       
        for(int i=head[s];i;i=q[i].link){
            int v=q[i].v;
            if(v==fa) continue;
            int tp=cnt,tp2=-1,id=-1;
            if(val[v]>st[cnt]){
                id=find(1,cnt,val[v]);
                tp2=st[id];
                st[id]=val[v];
                cnt=id;
            }
            else {
                id=cnt+1,tp2=st[cnt+1];
                st[++cnt]=val[v];
            }
            dfs(v,s);
            if(tp2!=-1&&id!=-1){st[id]=tp2;}
            cnt=tp;
        }
        ans[s]=cnt;
    }
    int main(){
        n=read();
        for(int i=2;i<=n;i++){
           f[i]=read();
           put(f[i],i);
        }
        for(int i=1;i<=n;i++){
            val[i]=read();
        }
        st[1]=val[1],cnt=1;
        dfs(1,0);
        for(int i=1;i<=n;i++){
            printf("%d ",ans[i]);
        }
    }

    T3 树

    题目大意:给定一棵$n$个节点的树,有$q$次操作,分别为:1.新增一个关键点2.将所有点重置为非关键点3.询问一个点是否为关键点。关键点每秒钟会将与它相邻的非关键点置为关键点,每次操作占一秒。

    根据题意,有$dep_v+dep_x-2 imes dep_{lca}leq now-tim_v$。移项,有$tim_v+dep_v-2 imes dep_{lca}leq now-dep_x$。然后我们可以把询问挂在lca上,就可以树链剖分区间修改区间查询了。

    此题的套路跟[LNOI2014]LCA这道题相似,都是把修改挂在lca上然后查询。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N=100005;
    const int inf=0x3f3f3f3f;
    int size[N],son[N],fa[N],dep[N],n,q;
    int dfn[N],top[N],idx[N],tot;
    int minn[N<<2],tag[N<<2],clr[N<<2];
    int head[N],cnt;
    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;
        fa[now]=f;dep[now]=dep[f]+1;
        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)
    {
        top[now]=topf;
        dfn[now]=++tot;idx[tot]=now;
        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 x,int l,int r)
    {
        int mid=(l+r)>>1;
        if (clr[x])
        {
            minn[x<<1]=minn[x<<1|1]=inf;
            clr[x<<1]=clr[x<<1|1]=inf;
            tag[x<<1]=tag[x<<1|1]=0;
            clr[x]=0;
        }
        if (tag[x])
        {
            minn[x<<1]=min(minn[x<<1],tag[x]-2*dep[idx[mid]]);
            minn[x<<1|1]=min(minn[x<<1|1],tag[x]-2*dep[idx[r]]);
            if (!tag[x<<1]) tag[x<<1]=tag[x];
            else tag[x<<1]=min(tag[x<<1],tag[x]);
            if (!tag[x<<1|1]) tag[x<<1|1]=tag[x];
            else tag[x<<1|1]=min(tag[x<<1|1],tag[x]);
            tag[x]=0;
        }
    }
    inline void update(int x,int l,int r,int ql,int qr,int k)
    {
        if (ql<=l&&r<=qr)
        {
            if (k!=inf)
            {
                minn[x]=min(minn[x],k-2*dep[idx[r]]);
                if (!tag[x]) tag[x]=k;
                else tag[x]=min(tag[x],k);
            }
            else clr[x]=minn[x]=inf,tag[x]=0;
            return;
        }
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if (ql<=mid) update(x<<1,l,mid,ql,qr,k);
        if (qr>mid) update(x<<1|1,mid+1,r,ql,qr,k);
        minn[x]=min(minn[x<<1],minn[x<<1|1]);
    }
    inline int query(int x,int l,int r,int ql,int qr)
    {
        if (ql<=l&&r<=qr) return minn[x];
        pushdown(x,l,r);
        int mid=(l+r)>>1;
        if (qr<=mid) return query(x<<1,l,mid,ql,qr);
        else if (ql>mid) return query(x<<1|1,mid+1,r,ql,qr);
        else return min(query(x<<1,l,mid,ql,qr),query(x<<1|1,mid+1,r,ql,qr)); 
    }
    inline void updrange(int x,int v)
    {
        int t=dep[x]+v;
        while(top[x]!=1)
        {
            update(1,1,n,dfn[top[x]],dfn[x],t);
            x=fa[top[x]];
        }
        update(1,1,n,1,dfn[x],t);
    }
    inline void qrange(int x,int v)
    {
        int t=v-dep[x];
        while(top[x]!=1)
        {
            int tmp=query(1,1,n,dfn[top[x]],dfn[x]);
            if (tmp<=t){
                puts("wrxcsd");return;
            }
            x=fa[top[x]];
        }
        int tmp=query(1,1,n,1,dfn[x]);
        if (tmp<=t) puts("wrxcsd");
        else puts("orzFsYo");
    }
    int main()
    {
        memset(minn,0x3f,sizeof(minn));
        n=read();q=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);
        for (int i=1;i<=q;i++)
        {
            int opt=read(),x=read();
            if (opt==1) updrange(x,i);
            if (opt==2) update(1,1,n,1,n,inf);
            if (opt==3) qrange(x,i);
        }
        return 0;
    }

    T4 魔塔

    题目大意:给定一棵$n$个节点的树,$1$为根节点。人在根节点。除根节点外每个点有怪,分别有血量,攻击,防御,蓝宝石四个属性。对于角色造成的伤害为攻击值减去防御值。当角色的血量小于等于0时就会死亡。蓝宝石可以增加相应的防御力。打过怪的地方不会再有新的怪。问人如何打怪使得最后血量最大。

    设性价比为蓝宝石数量与打怪次数之比,显然我们要先取大的(因为我们要尽量提升自己防御力同时掉血尽量少)。扔到大根堆里,每次取最大的。如果暂时无法访问到(因为是树形结构,有访问的先后顺序)就跟它的父亲合并。然后把更新过后的父亲(未被访问)扔进堆里即可。

    代码:

    #include<queue>
    #include<cstdio>
    #include<iostream>
    #define int long long
    #define ll long long
    using namespace std;
    const int N=100005;
    priority_queue< pair<double,int> > q;
    int fa[N],f[N],n;
    ll Hp[N],Ad[N],Def[N],Num[N],tim[N];
    bool vis[N];
    int head[N],cnt;
    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(int now,int father)
    {
        for (int i=head[now];i;i=edge[i].next)
            if (edge[i].to!=father){
                fa[edge[i].to]=now;
                dfs(edge[i].to,now);
            } 
    }
    inline int find(int x)
    {
        if (f[x]==x) return x;
        return f[x]=find(f[x]);
    }
    signed main()
    {
        n=read();
        for (int i=1;i<n;i++){
            int x=read(),y=read();
            add(x,y);add(y,x);
        }
        Hp[1]=read(),Ad[1]=read(),Def[1]=read();f[1]=1;
        for (int i=2;i<=n;i++)
        {
            Hp[i]=read(),Ad[i]=read(),Def[i]=read();Num[i]=read();f[i]=i;
            tim[i]=Hp[i]/(Ad[1]-Def[i]);
            if (Hp[i]%(Ad[1]-Def[i])==0) tim[i]--;
            Hp[1]-=(Ad[i]-Def[1])*tim[i];
            q.push(make_pair(1.0*Num[i]/tim[i],i)); 
        }
        dfs(1,0);
        vis[1]=1;
         while (!q.empty()) {
            int now=q.top().second;
            q.pop();
            if (vis[now]) continue;
            vis[now]=1;
            int X=find(fa[now]);
            Hp[1]+=tim[now]*Num[X];
            tim[X]+=tim[now];
            Num[X]+=Num[now];
            if (!vis[X]) q.push(make_pair(1.0*Num[X]/tim[X],X));
            f[now]=X;
        }
        printf("%lld",Hp[1]);
        return 0;
    }
  • 相关阅读:
    浅涉OPC Client
    枚举目标机器已注册的OPC服务器
    C++ DCOM服务器和C#客户端互操作完全解释
    COMException:没有注册类别(REGDB_E_CLASSNOTREG)
    网络化广播主机ZABKZ/AXT8182
    OPC 技术文档之 IOPCBrowseServerAddressSpace 的使用
    SQL Server 2008 r2 服务无法启动
    Infinova V2040 系列 大型矩阵切换/控制系统
    COM中GUID和UUID、CLSID、IID
    django 视图与网址
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13924035.html
Copyright © 2011-2022 走看看