[BZOJ3218]a + b Problem

壹、题目

传送门 to Darkbzoj

贰、思考

似乎和文理分科有点像,但是这个题的要求是有异色的是 "奇怪" 的。

考虑正难则反,如果一个点是奇怪的,那么它的贡献就是 \(b_i-p_i\),反之,如果它不是奇怪的,就是 \(b_i\) 了,如果要求一个点不是 "奇怪" 的,那么就要求所有满足 \(1\le j<i,l_i\le a_j\le r_i\) 的点都是黑色,也就是说如果这些点都是黑色,就会有额外的贡献。

这就和文理分科十分像了,考虑源点黑,汇点白,划分两个点集,首先从 \(s\) 向每个点连接 \(b_i-p_i\) 的边,意味着选择黑色,然后从每个点向汇点连接 \(w_i\) 的边,意味着选择白色,考虑都是黑色的,就建虚点,对于每个虚点,从源点连接一条边权为对应格子的 \(p_i\),然后从这个虚点向每个满足条件的格子连接一个 \(+\infty\),然后直接跑最大流求最小割,最后的答案就是所有的点减去最小割。

然后这种方法 \(\tt MLE+TLE\) 了 去吔屎吧,然鹅什么都布吉岛的我布吉岛怎么去优化,然后我 \(\tt GG\) 了。

叁、题解

好不容易想出来建图,然后布吉岛怎么优化......

考虑我们原先的建图方式需要多少空间?\(\mathcal O(n^2)\),那还是去自闭吧......

使用传说中的线段树优化建图,但是是二维偏序,所以还要考虑可持久化,而在线段树上的边,需要注意的是全部的值都应该赋为 \(+\infty\) 表示不可断掉。

可耻就话的时候不仅要继承点,还要继承边,具体实现看代码。

肆、代码

还没跳出来,然鹅不想调了,先留个坑在这里吧......

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
namespace Elaina{
    #define rep(i,l,r) for(int i=l,i##_end_=r;i<=i##_end_;++i)
    #define fep(i,l,r) for(int i=l,i##_end_=r;i>=i##_end_;--i)
    #define fi first
    #define se second
    #define Endl putchar('\n')
    #define writc(x, c) fwrit(x),putchar(c)
    #define mp(x,y) make_pair(x,y)
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef unsigned long long ull;
    typedef unsigned int uint;
    template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
    template<class T>inline T Min(const T x, const T y){return x<y?x:y;}
    template<class T>inline T fab(const T x){return x<0?-x:x;}
    template<class T>inline void getMax(T& x, const T y){x=Max(x,y);}
    template<class T>inline void getMin(T& x, const T y){x=Min(x,y);}
    template<class T>T gcd(const T x,const T y){return y?gcd(y,x%y):x;}
    template<class T>inline T readin(T x){
        x=0;int f = 0;char c;
        while((c=getchar())<'0'||'9'<c)if(c=='-')f=1;
        for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
        return f?-x:x;
    }
    template<class T>void fwrit(const T x){
        if(x<0)return putchar('-'),fwrit(-x);
        if(x>9)fwrit(x/10);putchar(x%10^48);
    }
}
using namespace Elaina;

const int maxn=5e3;
const int inf=(1<<30)-1;
int a[maxn+5],b[maxn+5],w[maxn+5],l[maxn+5],r[maxn+5],p[maxn+5];
int n,maxa;

ll ans=0;

inline void input(){
    n=readin(1);
    rep(i,1,n)a[i]=readin(1),b[i]=readin(1),w[i]=readin(1),l[i]=readin(1),r[i]=readin(1),p[i]=readin(1);
    rep(i,1,n)maxa=Max(maxa,a[i]);
}

struct edge{int to,nxt,c;
    edge(const int T=0,const int N=0,const int C=0):to(T),nxt(N),c(C){}
}e[maxn*100+5];
int tail[maxn*50+5],ecnt;
int cur[maxn*50+5];
int ncnt;
inline void add_edge(const int u,const int v,const int c){
    // printf("add_edge :> u == %d, v == %d, c == %d\n",u,v,c);
    // printf("%d %d %d\n",u,v,c);
    e[++ecnt]=edge(v,tail[u],c);tail[u]=ecnt;
    e[++ecnt]=edge(u,tail[v],0);tail[v]=ecnt;
}
inline void update(const int i,const int x){
    e[i].c-=x,e[i^1].c+=x;
}

int rt[maxn+5];
int ls[maxn*50+5],rs[maxn*50+5];
#define mid ((l+r)>>1)
void modify(int& i,const int pre,const int p,const int a,const int l=1,const int r=maxa){
    i=++ncnt;
    ls[i]=ls[pre],rs[i]=rs[pre];
    if(pre)add_edge(i,pre,inf);
    // printf("modify :> i == %d, pre == %d, p == %d, a == %d, l == %d, r == %d\n",i,pre,p,a,l,r);
    if(l==r)return add_edge(i,p,inf);
    if(a<=mid){
        modify(ls[i],ls[pre],p,a,l,mid);
        add_edge(i,ls[i],inf);
    }
    else{
        modify(rs[i],rs[pre],p,a,mid+1,r);
        add_edge(i,rs[i],inf);
    }
}
void query(const int L,const int R,const int p,const int i,const int l=1,const int r=maxa){
    // printf("query :> L == %d, R == %d, p == %d, i == %d, l == %d, r == %d\n",L,R,p,i,l,r);
    if(L>R)return;
    if(!i)return;
    if(L<=l && r<=R)return add_edge(p,i,inf);
    if(L<=mid)query(L,R,p,ls[i],l,mid);
    if(mid<R)query(L,R,p,rs[i],mid+1,r);
}

inline void build(){
    memset(tail,-1,sizeof tail);ecnt=-1;
    rep(i,1,n){
        ans=ans+b[i]+w[i]+p[i];
        add_edge(0,i,b[i]);
        add_edge(i,n+1,w[i]+p[i]);
    }
    // puts("finished first part");
    // printf("initial :> ans == %lld\n",ans);
    ncnt=n+1;
    rep(i,1,n){
        // printf("i == %d, begin modify\n",i);
        if(i>1)modify(rt[i],rt[i-1],i-1,a[i-1]);
        // printf("i == %d, end modofy\n",i);
        int nde=++ncnt;
        add_edge(0,nde,p[i]);
        add_edge(nde,i,inf);
        query(l[i],r[i],nde,rt[i]);
    }
    // rep(i,1,n){
    //     int nde=++ncnt;
    //     add_edge(0,nde,p[i]);
    //     add_edge(nde,i,inf);
    //     for(int j=1;j<i;++j)if(l[i]<=a[j] && a[j]<=r[i])
    //         add_edge(nde,j,inf);
    // }
}

int dis[maxn*50+5];
queue<int>Q;
inline int bfs(){
    memset(dis,-1,(ncnt+1)<<2);
    while(!Q.empty())Q.pop();
    dis[0]=0;Q.push(0);
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=tail[u],v;~i;i=e[i].nxt)if(e[i].c){
            v=e[i].to;
            if(dis[v]==-1){
                dis[v]=dis[u]+1;
                Q.push(v);
            }
        }
    }
    return dis[n+1]!=-1;
}
int dfs(const int u,const int flow,const int t){
    if(u==t)return flow;
    int rest=flow;
    for(int& i=cur[u];~i;i=e[i].nxt)if(e[i].c){
        int v=e[i].to;
        if(dis[v]==dis[u]+1){
            int k=dfs(v,Min(rest,e[i].c),t);
            rest-=k,update(i,k);
            if(rest==0)break;
        }
    }
    return flow-rest;
}
inline void dinic(){
    while(bfs()){
        memcpy(cur,tail,(ncnt+1)<<2);
        ans-=dfs(0,inf,n+1);
    }
    writc(ans,'\n');
}

signed main(){
    input();
    build();
    dinic();
    return 0;
}

伍、思路の一些问题及补救措施

我们加边时要加容量为 \(b_i-p_i\) 的边,会出现负容量,这个其实是有补救措施的。

至于怎么改要用待定系数法,细节留坑待填。

上一篇:PHP常见面试题大全


下一篇:AtCoder 总结