Codeforces Round#333 div.1 E A Museum Robbery

2 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:要你动态维护一个0-1背包问题(可以离线)。
首先预处理出每个背包所能影响的询问区间,然后区间插入询问的线段树中,注意线段树是分治的询问。
然后遍历线段树的每一个节点,遍历的过程中就只有加背包了,直接维护dp数组即可。
复杂度大概是O(nklogn)这个级别的。
这种算法之所以比暴力优,是因为一个背包会影响多个询问,而暴力每次都要重新考虑。
这种考虑的方式貌似可以适应很多问题,我去看看HNOI2016network能不能这样做。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<bitset>
#include<utility>
#include<functional>
#include<iomanip>
#include<sstream>
#include<ctime>
#include<cassert>
#define X first
#define INF 0x3f3f3f3f
#define Y second
#define LL long long
#define DB double
#define pii pair<int,int>
#define pll pair<LL,LL>
#define MP make_pair
#define pb push_back
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
template<class T>void Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
const int MAXN=20000,MAXK=1010;
const int p=1e7+19,q=1e9+7;
pii vm[MAXN],zeit[MAXN];
int dp[30][MAXK],n,k,Q;
vector<int> query;
int tot=0;
#define lc now<<1
#define rc now<<1|1
struct Segment_Tree{
    int first[40*MAXN],next[40*MAXN],e,to[40*MAXN];
    Segment_Tree()
    {
        memset(first,-1,sizeof(first));
        e=0;
    }
    void addEdge(int u,int v)
    {
        ++e;next[e]=first[u];first[u]=e;to[e]=v;
    }
    void add(int now,int l,int r,int ql,int qr,int v)
    {
        if(ql>qr)return;
        if(ql<=l&&qr>=r)
        {
            addEdge(now,v);
            return;
        }
        int mid=(l+r)>>1;
        if(ql<=mid)
            add(lc,l,mid,ql,qr,v);
        if(qr>mid)
            add(rc,mid+1,r,ql,qr,v);
    }
    void solve(int now,int l,int r,int dep)
    {
        memcpy(dp[dep],dp[dep-1],sizeof(dp[dep-1]));
        for(int i=first[now];i!=-1;i=next[i])
        {
            int v=to[i];
            for(int j=k;j>=vm[v].Y;j--)
                dp[dep][j]=max(dp[dep][j],dp[dep][j-vm[v].Y]+vm[v].X);
        }
        if(l==r)
        {
            LL ans=0,tmp=1;
            for(int i=1;i<=k;i++)
            {
                ans=(ans+dp[dep][i]*tmp%q)%q;
                tmp=tmp*p%q;
            }
            printf("%d\n",ans);
            return;
        }
        int mid=(l+r)>>1;
        solve(lc,l,mid,dep+1);
        solve(rc,mid+1,r,dep+1);
    }
}T;
int main()
{
    //freopen("5.in","r",stdin);
    //freopen("5.out","w",stdout);
    Read(n),Read(k);
    for(int i=1;i<=n;i++)
    {
        ++tot;
        int v,w;
        Read(v),Read(w);
        vm[tot].X=v,vm[tot].Y=w;
        zeit[tot].X=1;
        zeit[tot].Y=-1;
    }
    Read(Q);
    int lover=0;
    for(int i=1;i<=Q;i++)
    {
        int op;
        Read(op);
        if(op==1)
        {
            int v,w;
            Read(v),Read(w);
            vm[++tot]=MP(v,w);
            zeit[tot].X=lover+1;
            zeit[tot].Y=-1;
        }
        else if(op==2)
        {
            int x;
            Read(x);
            zeit[x].Y=lover;
        }
        else
            lover++;
    }       
    for(int i=1;i<=tot;i++)
    {
        if(zeit[i].Y==-1)
            zeit[i].Y=lover;
        T.add(1,1,lover,zeit[i].X,zeit[i].Y,i);
    }
    T.solve(1,1,lover,1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值