2019.01.02-dtoj-4100-yjqb

题目描述:

给定一个二分图,两个部分我们称之为A部和B部。
对于一个A部的点A,其在B部中相邻的点是一个连续的区间,记为[Li,Ri]。
现在你需要找一个尽量大的匹配,使之在具有匹配的性质的前提下,所有匹配边互不相交。(即不存在两条匹配边(Ai,Bx),(Aj,By),使得(i<j,x>y))。

算法标签:dp,splay

思路:

令f[i][j],i表示A部匹配到第i个,j表示B部匹配到第j个

有以下两个转移式:

f[i][j]=f[i-1][j];
对于li≤j≤ri,f[i][j]=f[i-1][j-1]+1;

我们发现两个式子都只与i-1有关,于是我们可以把第二维消去。观察发现,每一位与前一位的差值只为0或1。于是考虑差分,并且用splay维护区间关系。

每次把ri后的第一个1删去,没有1则删去最后一个0,在li的位置上加上一个1,表示把整个[Li,Ri]区间右移一格,并且选中区间的第一个比前一个多1。

统计答案看总共有多少个1即可。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5;
int n,l[N],r[N],rt,sum[N],son[N][2],sz[N],val[N],fa[N];
il int read(){int x;char ch;_(!);x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return x;}
il void update(int x){
    sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
    sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x];
}
il void build(int x,int l,int r){
    int mid=(l+r)>>1;fa[mid]=x;
    if(mid<x)son[x][0]=mid;else son[x][1]=mid;
    if(l<mid)build(mid,l,mid-1);
    if(mid<r)build(mid,mid+1,r);
    sz[mid]=sz[son[mid][0]]+sz[son[mid][1]]+1;
}
il void rotate(int x,int &k){
    int y=fa[x],z=fa[y],tp=(son[y][1]==x);
    if(y!=k)son[z][son[z][1]==y]=x;else k=x;
    son[y][tp]=son[x][tp^1];fa[son[y][tp]]=y;
    son[x][tp^1]=y;fa[y]=x;fa[x]=z;
    sz[x]=sz[y];sum[x]=sum[y];
    update(y);
}
il void splay(int x,int &k){
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k)((son[y][0]==x)^(son[z][0]==y))?rotate(x,k):rotate(y,k);
        rotate(x,k);
    }
}
il int kth(int x,int k){
    if(sz[son[x][0]]+1==k)return x;
    if(sz[son[x][0]]>=k)return kth(son[x][0],k);
    return kth(son[x][1],k-1-sz[son[x][0]]);
}
il int fp(int x){
    if(sum[son[x][0]])return fp(son[x][0]);
    if(val[x])return x;
    return fp(son[x][1]);
}
il void del(int x){   
    splay(x,rt);
    int rk=sz[son[x][0]]+1;
    int a=kth(rt,rk-1),b=kth(rt,rk+1);
    splay(a,rt);splay(b,son[a][1]);
    if(val[x])sum[a]--,sum[b]--,val[x]=0;
    son[b][0]=sz[x]=fa[x]=0;sz[a]--;sz[b]--;
}
int main()
{
  n=read();for(int i=1;i<=n;i++)l[i]=read(),r[i]=read();
  build(0,1,n+2);rt=(n+3)>>1;
    for(int i=1;i<=n;i++){
        int x=kth(rt,r[i]);
        splay(x,rt);
        if(sum[son[x][1]])x=fp(son[x][1]);
        else while(son[x][1])x=son[x][1];
        del(x);
        int a=kth(rt,l[i]),b=kth(rt,l[i]+1);
        splay(a,rt);splay(b,son[a][1]);
        son[b][0]=x;val[x]=sum[x]=sz[x]=1;
        sum[a]++;sum[b]++;sz[a]++;sz[b]++;fa[x]=b;
    }
    printf("%d\n",sum[rt]);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Jessie-/p/10211995.html