• NOI2015 小园丁和老司机


    题目链接

    洛谷的,有点不清楚。

    建议去UOJ上或LOJ切了再搬运。

    luogu

    UOJ

    LOJ

    sol

    老司机是一个显然的(dp)

    发现是个(DAG),可以愉快的按(y)坐标排序转移。

    发现五个移动不好算,那么设(g[i])表示从(i)点开始前往(y)坐标比(i)大的点的最多到的许愿树个数,(f[i])表示从(i)进入(y)坐标能够最多到的许愿树个数。

    先算这一层所有的(g),再算(f)。分两种情况,入点在出点右边或左边,只要从前往后(dp)一次,从后往前(dp)一次就(ok)了。

    (g)的话维护三个桶,分别代表(x+y),(x-y),(x)的桶,用(f)转移(g)

    因为如果(i<j,y_i==y_j),从(i)点进(j)点出可以先往左走,再往右走到(j)离开,所以算的时候贡献是(y)坐标为(y_i)的点的个数减去(j)和它右边的点的个数,那么我们可以一开始减掉,最后算答案的时候加上(y)坐标为(y_i)的点的个数。这显然是个前缀或后缀(max)的优化(dp)

    那么我们得到了(20 pts QAQ)

    (40)分的话也好做,(dfs)一条路出来,你怎么转移的就倒过来怎么找路径,具体可以看代码。

    (100)分不难,按(40)分的思路去找所有的边,只是不(dfs)而是遍历每个可能出现的点。

    然后就是个很裸的上下界最小流了。

    (litble)说直接写会(TLE),所以我就没写最裸的,加了个优化。

    因为你肯定有解,并且(S)(T)到每个点都有(inf)的边,所以我们不管它们,直接建超级源汇连原图,跑出来最大流就是图本生可以减少的流量。

    减掉就(ok)了。

    不算网络流模板这道题还挺短的(QAQ)

    #include<map>
    #include<algorithm>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #define gt getchar()
    #define ll long long
    #define Set(p,v) memset(p,v,sizeof(p))
    #define Cpy(p,v) memcpy(p,v,sizeof(p))
    inline int in()
    {
    	int k=0,p=1;char ch=gt;
    	while(!isdigit(ch)&&ch!='-')ch=gt;
    	if(ch=='-')p=0,ch=gt;
    	while(isdigit(ch))k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    inline int min(const int &a,const int &b){return a<b?a:b;}
    inline int max(const int &a,const int &b){return a>b?a:b;}
    const int inf=0x7fffffff;
    class Graph
    {
    private:
    	static const int N=400005,M=1000005;
    	int s,t;
    	int dis[N];
    	int	pre[N],pr[N],o[N],cur[N];
    	int head[N],nxt[M],to[M],cap[M];double w[M];
    public:
    	int n,cnt;
    	inline void init(int nn,int ss,int tt){n=nn,s=ss,t=tt;cnt=-1;Set(head,-1);}
    	inline void add_Edge(int u,int v,int cc,int ww){to[++cnt]=v,cap[cnt]=cc,w[cnt]=ww,nxt[cnt]=head[u],head[u]=cnt;}
    	inline void add(int u,int v,int cc,int ww=0){add_Edge(u,v,cc,ww),add_Edge(v,u,0,-ww);}
    	int dfs(int u,int dist)
    		{
    			if(u==t)return dist;
    			for(int &i=cur[u];~i;i=nxt[i])
    			{
    				if(dis[to[i]]==dis[u]+1&&cap[i]!=0)
    				{
    					int d=dfs(to[i],min(dist,cap[i]));
    					if(d>0){cap[i]-=d;cap[i^1]+=d;return d;}
    				}
    			}
    			return 0;
    		}
    	int bfs()
    		{
    			std::queue<int>Q;Set(dis,0);
    			Q.push(s);dis[s]=1;
    			while(!Q.empty())
    			{
    				int u=Q.front();Q.pop();
    				for(int i=head[u];~i;i=nxt[i])
    				{
    					if(cap[i]>0&&dis[to[i]]==0)
    					{
    						dis[to[i]]=dis[u]+1;
    						Q.push(to[i]);
    					}
    				}
    			}
    			return dis[t]>0;
    		}
    	int dinic()
    		{
    			int res=0,d;
    			while(bfs()){Cpy(cur,head);while((d=dfs(s,inf)))res+=d;}
    			return res;
    		}
    	int spfa()
    		{
    			//实数流的时候务必用for给dis赋值
    			std::queue<int>Q;Set(dis,0x3f);Set(pr,0);Set(o,0);Set(pre,0);
    			dis[s]=0;Q.push(s);o[s]=1;
    			while(!Q.empty())
    			{
    				int u=Q.front();Q.pop();o[u]=0;
    				for(int i=head[u];~i;i=nxt[i])
    					if(cap[i]>0&&dis[to[i]]>dis[u]+w[i])
    					{
    						dis[to[i]]=dis[u]+w[i];
    						pr[to[i]]=i;pre[to[i]]=u;
    						if(!o[to[i]])o[to[i]]=1,Q.push(to[i]);
    					}
    			}
    			return pre[t]!=0;
    		}
    	void fyl(int &ans,int &hh)
    		{
    			ans=hh=0;
    			while(spfa())
    			{
    				int d=inf;
    				for(int i=t;i!=s;i=pre[i])d=min(d,cap[pr[i]]);hh+=dis[t]*d;ans+=d;
    				for(int i=t;i!=s;i=pre[i])cap[pr[i]]-=d,cap[pr[i]^1]+=d;
    			}
    		}
    	int fyl()
    		{
    			int hh=0;
    			while(spfa())
    			{
    				int d=inf;
    				for(int i=t;i!=s;i=pre[i])d=min(d,cap[pr[i]]);hh+=dis[t]*d;
    				for(int i=t;i!=s;i=pre[i])cap[pr[i]]-=d,cap[pr[i]^1]+=d;
    			}
    			return hh;
    		}
    }cx;
    
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    const int N=50005;
    int n,tot;P E[N*20];
    struct point{int x,y,id;}a[N];
    inline bool cmpx(point a,point b){return a.x==b.x?a.y<b.y:a.x<b.x;}
    inline bool cmpy(point a,point b){return a.y==b.y?a.x<b.x:a.y<b.y;}
    inline bool cmpi(point a,point b){return a.id<b.id;}
    inline void cmax(int &x,int y,int *a,int &c,int b){if(x<y)x=y,a[(c=1)-1]=b;else if(x==y)a[c++]=b;}
    inline void cmax(int &x,int y){if(x<y)x=y;}
    inline void Push(int x,int y){E[++tot]=mk(x,y);}
    
    namespace w1
    {
    	using std::map;
    	map<int,int>t1,t2,t3,mp[N];
    	int f[N],g[N],pre[N][3],L[N],R[N],TT,o[N],cnt[N];
    	inline int calc(int u,int k)
    	{
    		if(u==k)return g[k];
    		if(u<k)return g[k]+k-L[k];
    		else return g[k]+R[k]-k;
    	}
    	void dfs(int val,int u)//求一组最优方案
    	{
    		if(u==-1)return;
    		if(u!=1)printf("%d ",a[u].id);
    		int l=L[u],r=R[u];
    		for(int i=l;i<=r;++i)
    			if(calc(u,i)==val)
    			{
    				if(u==i)return dfs(g[u]-1,pre[u][0]);
    				if(u<i)for(int j=u-1;j>=l;--j)printf("%d ",a[j].id);
    				else for(int j=u+1;j<=r;++j)printf("%d ",a[j].id);
    				if(u<i)for(int j=u+1;j<=i;++j)printf("%d ",a[j].id);
    				else for(int j=u-1;j>=i;--j)printf("%d ",a[j].id);
    				return dfs(g[i]-1,pre[i][0]);
    			}
    	}
    	void Dfs(int val,int u)//求所有边
    	{
    		if(u==-1||o[u])return;
    		int l=L[u],r=R[u];o[u]=1;
    		for(int i=l;i<=r;++i)
    			if(calc(u,i)==val)
    				for(int k=0;k<cnt[i];++k)
    					if(pre[i][k]&&pre[i][k]!=-1)
    					{
    						if(mp[i].find(k)==mp[i].end())Push(i,pre[i][k]),mp[i][k]=1;
    						if(!o[pre[i][k]])Dfs(g[i]-1,pre[i][k]);
    					}
    	}
    	void solve()
    	{
    		a[++n]=(point){0,0,0};std::sort(a+1,a+n+1,cmpy);
    		for(int i=n,j;i>=1;i=j-1)
    		{
    			for(j=i;j>1&&a[j-1].y==a[i].y;--j);
    			for(int k=j;k<=i;++k)L[k]=j,R[k]=i;
    			for(int k=j;k<=i;++k)
    			{
    				int x=a[k].x,y=a[k].y;g[k]=1;pre[k][cnt[k]=0]=-1;
    				if(t1.find(x+y)!=t1.end())cmax(g[k],f[t1[x+y]]+1,pre[k],cnt[k],t1[x+y]);
    				if(t2.find(x-y)!=t2.end())cmax(g[k],f[t2[x-y]]+1,pre[k],cnt[k],t2[x-y]);
    				if(t3.find( x )!=t3.end())cmax(g[k],f[t3[ x ]]+1,pre[k],cnt[k],t3[ x ]);
    				t1[x+y]=t2[x-y]=t3[x]=k;
    			}
    			for(int k=j,mx=-1e9;k<=i;++k)
    				cmax(f[k],g[k]),cmax(f[k],mx+i-j),cmax(mx,g[k]-k+j);
    			for(int k=i,mx=-1e9;k>=j;--k)
    				cmax(f[k],g[k]),cmax(f[k],mx+i-j),cmax(mx,g[k]-i+k);
    		}
    		printf("%d
    ",f[1]-1);dfs(f[1],1);puts("");Dfs(f[1],1);
    	}
    }
    
    namespace w2
    {
    	int du[N];
    	void solve()
    	{
    		int t=n+1,s=0,ans=0;cx.init(t+1,s,t);
    		for(int i=1;i<=tot;++i)
    			--du[E[i].fr],++du[E[i].sc],cx.add(E[i].fr,E[i].sc,inf);
    		for(int i=1;i<=n;++i)
    			if(du[i]>0)cx.add(s,i,du[i]),ans+=du[i];
    			else if(du[i]<0)cx.add(i,t,-du[i]);
    		printf("%d
    ",ans-cx.dinic());
    	}
    }
    int main()
    {
    	n=in();
    	for(int i=1;i<=n;++i)a[i].x=in(),a[i].y=in(),a[i].id=i;
    	w1::solve();w2::solve();
    	return 0;
    }
    
    
  • 相关阅读:
    jQuery 字符串拼接
    jQuery 筛选器 链式编程操作
    jQuery 常用操作
    jQuery介绍 常用选择器
    枚举-增加BOOL类型
    枚举-判断一年中第 125 天是工作日, 还是休息日?假设一年 365 天, 新年第一天是星期一。
    判断大小端序
    信息存储,共用体的应用
    栈的自实现
    strlen,strcat,strcpy,strcpm 自实现
  • 原文地址:https://www.cnblogs.com/cx233666/p/10422031.html
Copyright © 2020-2023  润新知