zoukankan      html  css  js  c++  java
  • bzoj1835[ZJOI2010]base基站选址

    据说正解是什么线段树优化DP,但是作为脑子有坑选手,我们需要5k的做法:

    主席树+决策单调性.....

    F[m][i]表示已经放置了m个基站,第m个基站放置在第i个村庄,第i个村庄及之前的村庄的总最少花费(包括建立基站的花费和赔偿的花费),转移的时候,F[m][i]=min(F[m-1][j]+cost(j,i))+ci

    Cost(j,i)表示在点j和点i各建立一个基站,j和i之间不建立基站时,j和i之间需要的总赔偿。

    考虑如何快速求出cost(j,i).对于每个村庄k我们可以在位置坐标轴上二分查找,预处理出一个区间[Lk,Rk],表示如果在[Lk,Rk]放一个基站就可以覆盖村庄k。那么只有[Lk,Rk]完全被[j,i]包含的时候才会产生花费.也就是说,问题转化成给定数轴上一堆带权区间,查询被某一个区间完全包含的所有区间的总权值。

    某一个区间被另一个区间完全包含,当且仅当这个区间的左右端点都被包含。所以查询可以等价于:对于右端点在查询范围内的区间,有多少对应的左端点大于查询范围的左端点。我们对每个位置建一棵权值线段树,存储从第一个位置到这个位置的所有区间的左端点,在对应的左端点的位置插入这个区间的权值。总之这个cost可以用主席树logn求。

    然后考虑分层转移。每次由设置了i个基站的dp数组推出一共设置i+1个基站的DP数组。

    接下来我们发现,这个东西有决策单调性(别问我怎么证,我打的表...),于是上单调栈。然后!考试的时候我的DP数组两维的大小开反了,成功炸掉80分。交换数组的两维大小之后A了,真是悲伤的故事….

    标算到底是啥

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    template<typename T>inline void read(T &x){
        char ch;while(ch=getchar(),!isdigit(ch));
        x=ch-'0';
        while(ch=getchar(),isdigit(ch))x=x*10+ch-'0';
    }
    
    const int maxn=20005,maxk=105;
    int f[maxk][maxn];
    int d[maxn],c[maxn],s[maxn],w[maxn];
    int l[maxn],r[maxn],seq[maxn];
    int n,k;
    struct node{
        int sum;node *lch,*rch;
    }t[maxn*50];int cnt=0;//11mb
    node *root[maxn];
    void Insert(node *rt1,node* &rt2,const int &k,const int &w,int l,int r){
        ++cnt;rt2=t+cnt;
        if(l==r){
            rt2->sum=rt1->sum+w;
            return;
        }
        int mid=(l+r)>>1;
        if(k<=mid){
            rt2->rch=rt1->rch;
            Insert(rt1->lch,rt2->lch,k,w,l,mid);
        }else{
            rt2->lch=rt1->lch;
            Insert(rt1->rch,rt2->rch,k,w,mid+1,r);
        }
        rt2->sum=rt2->lch->sum+rt2->rch->sum;
    }
    void build_all(){
        root[0]=t+0;root[0]->lch=root[0]->rch=t+0;
        int cur=0;
        for(int i=1;i<=n;++i){
            if(r[seq[i]]>cur){
                while(cur!=r[seq[i]]){
                    root[cur+1]=root[cur];
                    ++cur;
                }
            }
            Insert(root[cur],root[cur],l[seq[i]],w[seq[i]],1,n);
        }
        for(int i=cur+1;i<=n;++i)root[i]=root[i-1]; 
    }
    int query(node *rt1,node *rt2,int k,int l,int r){//有多少数字比k大 
       // printf("q%d %d",l,r);
        if(l>k){
            return rt2->sum-rt1->sum;
        }
        int mid=(l+r)>>1;
        if(k<mid)return query(rt1->lch,rt2->lch,k,l,mid)+query(rt1->rch,rt2->rch,k,mid+1,r);
        else return query(rt1->rch,rt2->rch,k,mid+1,r);
    }
    int qsum(int l,int r){//l,r各自有一个基站 
        return query(root[l],root[r-1],l,1,n);
    } 
    bool cmp(const int &a,const int &b){
        return r[a]<r[b];
    }
    bool cmp2(const int &a,const int &b){
        return l[a]<l[b];
    }
    int s1[maxn],s2[maxn]; 
    int st[maxn];int top=0,mk=0;//st:单调栈 
    int L[maxn],R[maxn];//每个决策对应的左右区间 
    inline bool inside(int i,int x){
        return L[x]<=i&&i<=R[x];
    }
    int binary(int l,int r,int x,int a,int b){//在f[l]..f[r]找出第一个决策b比决策a优的位置
        while(l<=r){
            int mid=(l+r)>>1;
            if(f[x-1][a]+qsum(a,mid)<f[x-1][b]+qsum(b,mid))l=mid+1;
            else r=mid-1;
        } 
        return r+1;
    }
    void work(int x){//x-1 -> x
        //使用x个基站,那么至少已经建到了x 
        top=0;mk=0;
    /*    st[++top]=x-1;mk=1;
        L[top]=x;R[top]=n;*/
        for(int i=x;i<=n;++i){
            while(top!=0&&(i-1)<L[top]&&((f[x-1][st[top]]+qsum(st[top],L[top]))>(f[x-1][i-1]+qsum(i-1,L[top])))){
                top--;
            }
            //binary
            if(top!=0){
                int tmp;
                if(i-1<L[top])tmp=binary(L[top],R[top],x,st[top],i-1);
                else tmp=binary(i,R[top],x,st[top],i-1);
                R[top]=tmp-1;
            }
            st[++top]=i-1;L[top]=R[top-1]+1;R[top]=n;
            while(!inside(i,mk))mk++;
            f[x][i]=c[i]+f[x-1][st[mk]]+qsum(st[mk],i);//printf("%d %d %d %d %d
    ",mk,x-1,st[mk],x,i);
        } 
    }
    int main(){
       // freopen("base.in","r",stdin);
      //  freopen("base.out","w",stdout);
        memset(f,0x7f,sizeof(f));
        read(n);read(k);
        for(int i=2;i<=n;++i){
            read(d[i]);
        }
        for(int i=1;i<=n;++i){
            read(c[i]);
        }
        for(int i=1;i<=n;++i){
            read(s[i]);
            l[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;r[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1;
        }
        for(int i=1;i<=n;++i)read(w[i]);
        for(int i=1;i<=n;++i)seq[i]=i;
        int sum=0;
        for(int i=1;i<=n;++i){
            sum+=w[i];
        }
        if(k==0){
            printf("%d
    ",sum);
        }else{
            sort(seq+1,seq+n+1,cmp);
            build_all();
            for(int i=1;i<=n;++i){
                s1[r[i]]+=w[i];
            }
            for(int i=1;i<=n;++i){
                s1[i]+=s1[i-1];
            }sort(seq+1,seq+n+1,cmp2);
            for(int i=1;i<=n;++i){
                s2[l[i]]+=w[i];
            }
            for(int i=n;i>=1;--i){
                s2[i]+=s2[i+1];
            }
            int ans=sum;
            for(int i=1;i<=n;++i){
                f[1][i]=s1[i-1]+c[i];
                int tmp=f[1][i]+s2[i+1];
                if(tmp<ans)ans=tmp;
            }
            for(int j=2;j<=k&&j<=n;++j){
                work(j);
            } 
            for(int i=1;i<=n;++i){
                for(int j=2;j<=k&&j<=n;++j){
                    if(f[j][i]+s2[i+1]<ans)ans=f[j][i]+s2[i+1];
                }
            }
            printf("%d
    ",ans); 
        }//while(1);
       // fclose(stdin);fclose(stdout);
        return 0;
    }
  • 相关阅读:
    hdu 4496 D-City 并查集
    hdu 4493 Tutor 水题
    codeforces 377A. Puzzles 水题
    hdu 1257 小希的迷宫 并查集
    图论500题
    cdoj 93 King's Sanctuary 傻逼几何题
    cdoj 题目简单分类
    cdoj 80 Cube 水题
    cdoj 71 I am Lord Voldemort 水题
    cdoj 65 CD Making 水题
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6051422.html
Copyright © 2011-2022 走看看