11月3日考试 题解 (背包+单调栈+树链剖分+贪心)

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

T1 软件

原题:洛谷P1800

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

时间复杂度$O(n^3\log 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(n\log 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\times dep_{lca}\leq now-tim_v$。移项,有$tim_v+dep_v-2\times 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;
}

 

posted @ 2020-11-04 08:13  我亦如此向往  阅读(117)  评论(0编辑  收藏  举报