heike - 霍尔定理 - 拟阵贪心

题目大意:
有n个人,T个物品,每个人有三个参数L,R,v,你可以从n个人中选出若干人,每个人分配恰好一个物品,并且满足对于每个人分配的物品编号必须在Li,Ri之间,并且没有两个人分配同一个物品。此时会获得选出来的人的v的和,要使得这个和最大。
另有m次询问,询问如果删除第i个人,或者加入一个[L,R,v]的人,注意询问是独立的,即询问玩就撤销了。 n , m 1 0 6 , T 300 n,m\le10^6,T\le300
题解:
先不考虑询问。
这是个二分图费用流。
其具有拟阵性质:即可以按照权值从大到小,能加就加,这样贪心可以证明正确性。

因此变为加入一个人,问是否存在完备匹配的问题。
根据霍尔定理:对于任意左集合X,必须有|X|<=|N(X)|,其中N(X)为X的邻集。
这个可以反过来:对于任意右集合Y,对于任意左集合X满足N(x)是Y的子集,必须有|X|<=|Y|。

注意到邻集是若干段区间,而且每一段区间可以分别考虑,因此一个朴素的判定是枚举右边的区间,看其包含多少人的区间,记作cnt,那么cnt[l,r]<=r-l+1。
同时记cnt[l,r]=r-l+1的区间为“满”区间。
直接判定能否加入一个人就是判断其“超区间”是否存在满区间。

有一个性质是两个满区间的并集和交集都是满区间。(*)
根据这个性质,可以这样判定能否加入一个人:
如果一个区间是满区间,那么将其区间涂黑。
那么一个人能加入,当且仅当这个人对应的区间存在白点。
意思是若这个人对应的区间全是黑点,意味着存在若干区间的并集包含这个人对应的区间。
所以这部分可以做到 O ( n + T 3 ) O(n+T^3)

考虑加入删除一个人。
首先显然删除一个人会让所有包含他的满区间变得不满。
首先我们定义G(X)表示包含x的最小的满区间。由于性质(*),这样的区间是唯一的,这个显然可以 O ( n + T 3 ) O(n+T^3) 预处理出。
考虑加入一个人,加入的人对应的区间记作X。
考虑若G(X)不存在,那么可以直接加入。
其次一定是顶替掉一个人(你可以类比最大生成树),假设顶替掉的是人对应的区间是Y,那么显然必须有Y是G(X)的子集才行。这个可以 O ( T 2 ) O(T^2) 预处理然后O(1)回答。

删除一个人,首先如果他在初始解中没有被删除那么可以直接不管。
否则删掉这个人记其对应区间是X,有可能会让另一个原来不在初始解中的人加进来,记其所对应的区间是Y,那么能够加入Y当且仅当G(X)是G(Y)的子集。
这个也可以 O ( n + T 2 ) O(n+T^2) 的预处理出。

因此这个题可以 O ( n + m + T 3 ) O(n+m+T^3) 的做完。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=1000010,MXT=310,INF=int(1.5e9);
struct P{ int l,r,v,id;inline bool operator<(const P &p)const{ return v<p.v; } }p[N];
pii g[MXT][MXT];int gv[MXT][MXT],gv2[MXT][MXT],col[MXT],id[N],cs[N],cnt[MXT][MXT];
char ss[30000000],tt[30];int ssl,ttl;
inline int show(int x)
{
    if(!x) ss[++ssl]='0';for(ttl=0;x;x/=10) tt[++ttl]=int(x%10+'0');
    for(;ttl;ttl--) ss[++ssl]=tt[ttl];return ss[++ssl]='\n';
}
int main()
{
    inn();int T=inn(),n=inn(),m=inn(),ans=0;
    rep(i,1,n) p[i].l=inn(),p[i].r=inn(),p[i].v=inn(),p[i].id=i;
    sort(p+1,p+n+1);rep(i,1,n/2) swap(p[i],p[n-i+1]);
    rep(i,1,T) rep(j,i,T) gv[i][j]=INF;rep(i,1,n) id[p[i].id]=i;
    rep(i,1,n)
    {
        int l=p[i].l,r=p[i].r,v=p[i].v,ok=0;
        rep(j,l,r) if(!col[j]) { ok=1;break; }
        if(!ok) continue;ans+=v,gv[l][r]=min(gv[l][r],v),cs[i]=1;
        rep(j,1,l) rep(k,r,T) if((++cnt[j][k])==k-j+1) rep(t,j,k) col[t]=1;
    }
    rep(len,2,T) rep(i,1,T-len+1) { int j=i+len-1;gv[i][j]=min(gv[i][j],min(gv[i][j-1],gv[i+1][j])); }
    for(int len=T,j;len;len--) rep(i,1,T-len+1)
        if(cnt[i][j=i+len-1]==len) g[i][j]=mp(len,i);
        else{
            g[i][j]=mp(INF,i);
            if(i>1) g[i][j]=min(g[i][j],g[i-1][j]);
            if(j<T) g[i][j]=min(g[i][j],g[i][j+1]);
        }
    rep(i,1,n) if(!cs[i])
    {
        int lp=p[i].l,rp=p[i].r,l=g[lp][rp].sec,r=l+g[lp][rp].fir-1;
        if(r<=T) gv2[l][r]=max(gv2[l][r],p[i].v);
    }
    for(int len=T,j;len;len--) rep(i,1,T-len+1)
        j=i+len-1,gv2[i][j]=max(gv2[i][j],max(gv2[i][j+1],gv2[i-1][j]));
    printf("%d\n",ans);
    while(m--)
        if(inn()==1)
        {
            int lp=inn(),rp=inn(),v=inn();
            int l=g[lp][rp].sec,r=l+g[lp][rp].fir-1;
            if(r<=T) show(ans+max(v-gv[l][r],0));
            else show(ans+v);
        }
        else{
            int i=id[inn()],lp=p[i].l,rp=p[i].r;
            int l=g[lp][rp].sec,r=l+g[lp][rp].fir-1;
            if(!cs[i]) show(ans);
            else if(r<=T) show(ans-p[i].v+gv2[l][r]);
            else show(ans-p[i].v);
        }
    return fwrite(ss+1,sizeof(char),ssl,stdout),0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/84306273