• NOI2019


    序列

    Luogu
    LOJ
    UOJ
    BZOJ
    可以直接贪心,但是用模拟费用流推的话会更轻松。
    首先有一个显然的建图方式:
    (S)(0)流量为(k),费用为(0)
    (0)(a_i)流量为(1),费用为(-a_i)
    (a_i)(b_i)流量为(1),费用为(0)
    (b_i)(T)流量为(1),费用为(-b_i)
    (a_i)(c)流量为(1),费用为(0)
    (c)(d)流量为(k-l),费用为(0)
    (d)(b_i)流量为(1),费用为(0)
    然后我们来模拟一下费用流。
    首先我们肯定先把(c->d)这里流满,因为这个是没有(a_i->b_i)的配对限制的,一定会更优。
    我们先在左右各选(k-l)(可能有重复的),能够直接匹配流(a_i->b_i),否则走(c->d)
    接下来我们先把(c->d)流满。
    也就是直接在(a,b)中各选一个最大的。
    然后我们接下来每次保证增加一对(a_i->b_i),这样就能够保证正确性了。
    这里有三种情况:
    第一种:我们选一个(a_i,b_i)都未选的(a_i+b_i)最大的。
    第二种:对于一个已被选择的(a_i)(对应的(b_i)尚未被选),我们给它(b_i)配对,并且在未被选的(a_j)中挑一个最大的。
    反应在流量网络上就是这样:
    原本是(a_i->c->d->b_x)(这个(b_x)实际上可以是任意一个流量从(d)来的点)。
    我们把它改成(a_i->b_i)(a_j->c->d->b_x)
    第三种:把第二种的(a,b)反过来。
    这样我们每次选取能够使答案增加最大的一种方法走,就能够解决这个问题了。
    具体而言,我们需要开几个堆记录未被选的(a_i,b_i,a_i+b_i)的最大值的下标(i),已选的(a_i(b_i))对应的(b_i(a_i))中最大值的下标(i)
    代码比较长,凑合着看吧。

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    namespace IO
    {
        char ibuf[(1<<21)+1],*iS,*iT;
        char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
        int read(){int x=0;char c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+(c^48),c=Get();return x;}
    }
    using namespace IO;
    int max(int a,int b){return a>b? a:b;}
    const int N=1000007;
    LL ans;int T,n,l,k,id[N],a[N],b[N],fa[N],fb[N];
    struct nodea{int id;};struct nodeb{int id;};struct nodec{int id;};
    int operator<(nodea i,nodea j){return a[i.id]<a[j.id];}
    int operator<(nodeb i,nodeb j){return b[i.id]<b[j.id];}
    int operator<(nodec i,nodec j){return a[i.id]+b[i.id]<a[j.id]+b[j.id];}
    int cmpa(int i,int j){return a[i]>a[j];}
    int cmpb(int i,int j){return b[i]>b[j];}
    priority_queue<nodea>ra,sa;priority_queue<nodeb>rb,sb;priority_queue<nodec>rc;
    int main()
    {
        int i,j,num,va,vb,vc,mx;
        for(T=read();T;--T)
        {
    	n=read(),k=read(),l=read(),memset(fa,0,sizeof fa),memset(fb,0,sizeof fb),ans=num=va=vb=vc=0;
    	while(!ra.empty())ra.pop();while(!rb.empty())rb.pop();while(!sa.empty())sa.pop();while(!sb.empty())sb.pop();while(!rc.empty())rc.pop();
    	for(i=1;i<=n;++i) a[i]=read(),id[i]=i;
    	for(i=1;i<=n;++i) b[i]=read();
    	sort(id+1,id+n+1,cmpa); for(i=1;i<=k-l;++i) ans+=a[id[i]],fa[id[i]]=1;
    	sort(id+1,id+n+1,cmpb); for(i=1;i<=k-l;++i) ans+=b[id[i]],fb[id[i]]=1;
    	for(i=1;i<=n;++i)
    	    if(fa[i]&&fb[i]) ++num;
    	    else if(fa[i]) rb.push((nodeb){i}),sb.push((nodeb){i});
    	    else if(fb[i]) ra.push((nodea){i}),sa.push((nodea){i});
    	    else ra.push((nodea){i}),rb.push((nodeb){i}),rc.push((nodec){i});
    	while(l--)
    	{
    	    while(!ra.empty()&&fa[ra.top().id])ra.pop();
    	    while(!rb.empty()&&fb[rb.top().id])rb.pop();
    	    while(!rc.empty()&&(fa[rc.top().id]||fb[rc.top().id]))rc.pop();
    	    while(!sa.empty()&&fa[sa.top().id])sa.pop();
    	    while(!sb.empty()&&fb[sb.top().id])sb.pop();
    	    if(num)
    	    {
    		i=ra.top().id,j=rb.top().id,ans+=a[i]+b[j],fa[i]=fb[j]=1,--num;
                    if(!fb[i]) sb.push((nodeb){i});
                    if(!fa[j]) sa.push((nodea){j});
                    if(i==j)++num;else{if(fa[i]&&fb[i])++num;if(fa[j]&&fb[j])++num;}
    		continue;
    	    }
    	    if(!sb.empty()) i=ra.top().id,j=sb.top().id,va=a[i]+b[j];
    	    if(!sa.empty()) i=rb.top().id,j=sa.top().id,vb=a[j]+b[i];
    	    if(!rc.empty()) i=rc.top().id,vc=a[i]+b[i];
    	    mx=max(vc,max(va,vb)),ans+=mx;
    	    if(va==mx) i=ra.top().id,j=sb.top().id,fa[i]=fb[j]=1,fb[i]? ++num:(sb.push((nodeb){i}),0);
    	    else if(vb==mx) i=rb.top().id,j=sa.top().id,fa[j]=fb[i]=1,fa[i]? ++num:(sa.push((nodea){i}),0);
    	    else if(vc==mx) i=rc.top().id,rc.pop(),fa[i]=fb[i]=1;
    	}
    	printf("%lld
    ",ans);
        }
    }
    

    当然如果我们一开始啥都不干(把“我们先在左右各选(k-l)(可能有重复的),能够直接匹配流(a_i->b_i),否则走(c->d)”这一步去掉)也是可行的,改几个地方就行了。
    代码会短一些,不过常数会大一些。

    弹跳

    Luogu
    LOJ
    UOJ
    BZOJ
    考虑在不把边显式地建出来的前提下模拟( ext{dijkstra})的过程。
    也就是初始有(n)个点,需要支持矩形覆盖、单点删除。
    那么我们可以线段树套set,其中线段树处理横坐标,set处理纵坐标。
    同时注意到我们只有删除没有插入,因此可以将set改成vector然后用并查集维护下一个未被删除的元素。

    #include<queue>
    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<numeric>
    #include<utility>
    #include<algorithm>
    #include<functional>
    using pi=std::pair<int,int>;
    const int N=70004,M=150007,Z=1<<17,S=12*M;
    int n,m,w,h,pos,dis[N],t[1<<18|7],fa[S],bel[S],id1[N][20],id2[M][40];char ibuf[1<<23],*iS=ibuf;pi dct[N+M];
    struct vertex{int x,y;}a[N];
    struct edge{int t,l,r,d,u;}e[M];
    std::vector<int>g[N],tmp;
    std::priority_queue<pi,std::vector<pi>,std::greater<pi>>q;
    int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    int find(int x){return x==fa[x]? x:fa[x]=find(fa[x]);}
    void insert(int*a,int x){a[++*a]=x;}
    void insert_vertex(int x,int i){for(x+=Z;x;x>>=1)insert(id1[i],t[x]),bel[t[x]++]=i;}
    void insert_edge(int l,int r,int i)
    {
        for(l+=Z-1,r+=Z+1;l^r^1;l>>=1,r>>=1)
        {
    	if(~l&1) insert(id2[i],t[l^1]);
    	if(r&1) insert(id2[i],t[r^1]);
        }
    }
    void del(int x,int i){for(x+=Z;x;x>>=1)fa[id1[i][pos]]=id1[i][pos]+1,++pos;}
    void work(int i){for(int p=id2[i][pos++];p=find(p),bel[p]&&a[bel[p]].y<=e[i].u;tmp.push_back(bel[p++]));}
    void query(int l,int r,int i)
    {
        for(l+=Z-1,r+=Z+1;l^r^1;l>>=1,r>>=1)
        {
    	if(~l&1) work(i);
    	if(r&1) work(i);
        }
    }
    int main()
    {
        fread(ibuf,1,1<<23,stdin);
        n=read(),m=read(),w=read(),h=read();
        for(int i=1;i<=n;++i) a[i]=vertex{read(),read()},dct[i]=pi(a[i].y,i),++t[a[i].x+Z];
        for(int i=1,u;i<=m;++i) u=read(),g[u].push_back(i),e[i]={read(),read(),read(),read(),read()},dct[i+n]=pi(e[i].d,-i);
        for(int i=Z-1;i;--i) t[i]=t[i<<1]+t[i<<1|1];
        for(int i=1;i<Z+Z;++i) t[i]+=t[i-1]+1;
        std::iota(fa+1,fa+t[Z+Z-1]+1,1);
        for(int i=Z+Z-1;i;--i) t[i]=t[i-1]+1;
        std::sort(dct+2,dct+n+m+1);
        for(int i=2,x;i<=n+m;++i) if((x=dct[i].second)>0) insert_vertex(a[x].x,x); else x=-x,insert_edge(e[x].l,e[x].r,x);
        for(int x:g[1]) q.emplace(e[x].t,x);
        while(!q.empty())
        {
    	int now=q.top().second,d=q.top().first;
    	q.pop(),tmp.clear(),pos=1,query(e[now].l,e[now].r,now);
    	for(int u:tmp)
    	{
    	    dis[u]=d,pos=1,del(a[u].x,u);
    	    for(int next:g[u]) q.emplace(dis[u]+e[next].t,next);
    	}
        }
        for(int i=2;i<=n;++i) printf("%d
    ",dis[i]);
    }
    
  • 相关阅读:
    C/C++网络编程2——socket函数
    C/C++网络编程1——linux下实现
    nginx_2_nginx进程模型
    nginx_1_初始nginx
    C++11并发编程4------线程间共享数据
    C++11并发编程3------线程传参
    C++11并发编程2------线程管理
    C++11并发编程1------并发介绍
    打造自己的开发环境
    gcc/g++/make/cmake/makefile/cmakelists的恩恩怨怨
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/13006125.html
Copyright © 2020-2023  润新知