zoukankan      html  css  js  c++  java
  • 【HMOI】小C的填数游戏 DP+线段树维护

      【题目描述】

        一个长为n的序列,每个元素有一个a[i],b[i],a[i]为0||1,每个点和他相邻的两个点分别有两条边,权值为cost1[i],cost2[i],对于每个区间l,r,我们可以给每一个数一个c[i]值,c[i]=0||1,使得Σ(b[j]*(a[j]^c[j]))+cost1[j]*(c[j]^c[j+1])+cost2[j]*(c[j]^c[j+2]),j,j+1,j+2在[l,r]内时累加,现在有m次操作:

          M x:将x位置的a[i]^=1;

          Q l r:询问区间l,r的答案。

      首先假设询问的只是一个区间,那么我们可以比较容易的用dp来求解w[i][s]表示到第i位的时候,后两位选的s的最小值,那么我们可以转移到w[i+1][ss]。这样就可以得到答案了。

       但是现在我们支持区间询问和修改,所以我们用线段树来维护,每个节点记录w[s],s为这个区间左面两个点c[i]的选取方法和右面两个点的选取方法,那么只要我们可以维护线段树每个节点的w[i],我们就可以解决这个问题了。

      对于合并操作比较简单,只需要枚举两个节点的s,然后更新的ss就可以了。

      反思:一个节点的情况需要特殊处理,开始没注意到这个,后来改了半天,合并的时候还分了三种情况讨论= =。

         还需要开LL,设inf的时候设的是#define inf (1<<60) 结果改了半天= =

         评测数据的5号评测点少了一个操作,然后我的输出就多了一行= =。

    //By BLADEVIL
    #include <cstdio>
    #include <cstring>
    #define min(x,y) (x>y)?y:x;
    #define inf (1ll<<60)
    #define maxn 30010
    #define LL long long
    
    using namespace std;
    
    struct rec {
        int left,right;
        LL key;
        LL w[20];
        rec() {
            left=right=0;
            key=inf;
            for (LL i=0;i<20;i++) w[i]=inf;
        }
    }t[maxn<<2];
    
    LL n,m;
    LL a[maxn],b[maxn],cost1[maxn],cost2[maxn];
    char s[10];
    
    void init(int x) {
        t[x].w[0]=b[t[x].left]*a[t[x].left];
        t[x].w[1]=b[t[x].left]*(a[t[x].left]^1);
        t[x].key=min(t[x].w[0],t[x].w[1]);
    }
    
    rec update(rec a,rec b) {
        rec ans;
        ans.left=a.left; ans.right=b.right;
        if ((a.left==a.right)&&(b.left==b.right))
            for (LL s=0;s<4;s++) {
                LL ss=s|(s<<2);
                LL c1=((s&2)==2)?1:0,c2=((s&1)==1)?1:0; 
                //if ((ss==3)&&(a.left==1)) printf("%d %d
    ",c1,c2);
                if ((a.w[c1]==inf)||(b.w[c2]==inf)) continue;
                ans.w[ss]=min(ans.w[ss],a.w[c1]+b.w[c2]+(c1^c2)*cost1[a.right]);
                //if ((ss==3)&&(a.left==1)&&(a.right==1)) printf("%d
    ",ans.w[ss]);
                ans.key=min(ans.key,ans.w[ss]);
                //if ((a.left==1)&&(a.right==1)) printf("%d %d
    ",ans.key,ss);
            } else 
        if (a.left==a.right)
            for (LL s1=0;s1<2;s1++)
                for (LL s2=0;s2<16;s2++) {
                    if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue;
                    LL s=0;
                    s|=s1<<3; s|=(s2&8)>>1; s|=s2&3;
                    LL c2=((s2&8)==8)?1:0,c3=((s2&4)==4)?1:0;
                    ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(s1^c2)*cost1[a.left]+(s1^c3)*cost2[a.left]);
                    ans.key=min(ans.key,ans.w[s]);
                } else 
        if (b.left==b.right)
            for (LL s1=0;s1<16;s1++)
                for (LL s2=0;s2<2;s2++) {
                    if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue;
                    LL s=0;
                    s|=s2; s|=(s1&1)<<1; s|=s1&12;
                    LL c1=((s1&2)==2)?1:0,c2=((s1&1)==1)?1:0;
                    ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(c1^s2)*cost2[a.right-1]+(c2^s2)*cost1[a.right]);
                    ans.key=min(ans.key,ans.w[s]);
                } else 
        if ((a.left!=a.right)&&(b.left!=b.right))
            for (LL s1=0;s1<16;s1++) 
                for (LL s2=0;s2<16;s2++) {
                    if ((a.w[s1]==inf)||(b.w[s2])==inf) continue;
                    LL s=(s1>>2)<<2;
                    s|=s2&3;
                    LL c1=((s1&2)==2)?1:0,c2=((s1&1)==1)?1:0;
                    LL c3=((s2&8)==8)?1:0,c4=((s2&4)==4)?1:0;
                    LL Ans=a.w[s1]+b.w[s2];
                    Ans+=cost1[a.right]*(c2^c3)+cost2[a.right]*(c2^c4)+cost2[a.right-1]*(c1^c3);
                    ans.w[s]=min(ans.w[s],Ans);
                    ans.key=min(ans.key,ans.w[s]);
                }
        return ans;
    }
    
    void build(int x,int l,int r) {
        t[x].left=l; t[x].right=r;
        if (t[x].left==t[x].right) {
            init(x);
            return ;
        }
        int mid=l+r>>1;
        build(x<<1,l,mid); build((x<<1)+1,mid+1,r);
        t[x]=update(t[x<<1],t[(x<<1)+1]);
    }
    
    rec query(int x,int l,int r) {
        if ((t[x].left==l)&&(t[x].right==r)) return t[x];
        int mid=t[x].left+t[x].right>>1;
        if (mid>=r) return query(x<<1,l,r); else 
        if (mid<l) return query((x<<1)+1,l,r); else 
            return update(query(x<<1,l,mid),query((x<<1)+1,mid+1,r));
    }
    
    void change(int x,int y) {
        if (t[x].left==t[x].right) {
            init(x);
            return ;
        }
        LL mid=t[x].left+t[x].right>>1;
        if (y>mid) change((x<<1)+1,y); else change(x<<1,y);
        t[x]=update(t[x<<1],t[(x<<1)+1]);
    }
    
    int main() {
        freopen("game.in","r",stdin); freopen("game.out","w",stdout);
        scanf("%lld%lld",&n,&m);
        for (LL i=1;i<=n;i++) scanf("%lld",&a[i]);
        for (LL i=1;i<=n;i++) scanf("%lld",&b[i]);
        for (LL i=1;i<n;i++) scanf("%lld",&cost1[i]);
        for (LL i=1;i<n-1;i++) scanf("%lld",&cost2[i]);
        build(1,1,n);
        /*
        for (LL s=0;s<16;s++) printf("%d %d
    ",s,query(1,1,3).w[s]);
        return 0;
        */
        //printf("%d
    ",query(1,2,4).key); return 0;
        while (m--) {
            scanf("%s",s);    
            LL x,y;
            if (s[0]=='Q') {
                scanf("%lld%lld",&x,&y);
                printf("%lld
    ",query(1,x,y).key);
            } else {
                scanf("%lld",&x);
                a[x]^=1;
                change(1,x);
            }
        }
        //for (LL s=0;s<16;s++) printf("%d %d
    ",s,query(1,1,4).w[s]);
        fclose(stdin); fclose(stdout);
        return 0;
    }
  • 相关阅读:
    C#中Excel的导入和导出的几种基本方式
    关于XML与类型Class的映射
    关于模板的相关注意事项
    OGRE 2.1 Windows 编译
    R6010 -abort() has been called
    VS2012 编译 Assimp
    C++ 并发消息队列
    Debian 安装下载工具软件
    Oracle SQL语句追踪
    记录自己学习网址
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3632245.html
Copyright © 2011-2022 走看看