1 条题解

  • 0
    @ 2023-4-6 21:15:46

    抽象至极的一道题。 第一问想到用网络流;第二问拆分函数并最大流求,分得越细,即△的值越小,精度就越优秀,但复杂度就越劣;合理控制△的值,可以拿到一定的分数。

    #include<bits/stdc++.h>
    #define x first
    #define y second
    using namespace std;
    typedef long long LL;
    const double eps=1e-8;
    const int N=1100,M=1100,INF=0x3f3f3f3f;
    inline int read(){
    	char c=getchar();int x=0,f=1;
    	while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
    	while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    
    int cnt=1,head[N+M];
    struct edge{int from,to,next; double val;}e[N*M<<1];
    inline void add(int f,int t,double v)
    	{e[++cnt]={f,t,head[f],v};head[f]=cnt;/*if(v)printf("%d %d %.1lf\n",f,t,(v && v<=10)? v:0);*/}
    inline LL gcd(LL a,LL b)
    	{while(b){a%=b; a^=b; b^=a; a^=b;}return a;}
    inline void initiator()
    	{memset(head,0,sizeof head); cnt=1;}
    struct frac		//val=x/y
    {
    	LL x,y;
    	frac(){x=0;	y=1;}
    	frac(LL a,LL b=1):x(a),y(b)
    	{
    		LL g=gcd(a,b);
    		x/=g;	y/=g;
    	}
    	inline double value(){return (double)x/y;}
    };
    typedef pair<frac,frac> point;
    struct line{frac k,b;};			//y=kx+b
    inline frac simplify(frac a)
    {
    	if(!a.x) return frac(0,1);
    	if(a.y<0)	a.x=-a.x,a.y=-a.y;
    	LL g=gcd(abs(a.x),a.y);
    }
    inline frac simplify(LL a,LL b){return simplify(frac(a,b));}
    inline frac operator+(frac a,frac b){return simplify(a.x*b.y+a.y*b.x,a.y*b.y);}
    inline frac operator-(frac a,frac b){return simplify(a.x*b.y-a.y*b.x,a.y*b.y);}
    inline frac operator*(frac a,frac b){return simplify(a.x*b.x,a.y*b.y);}
    inline frac operator/(frac a,frac b){return simplify(a.x*b.y,a.y*b.x);}
    inline bool operator==(frac a,frac b){return a.x==b.x && a.y==b.y;}
    inline bool operator==(line a,line b){return a.k==b.k && a.b==b.b;}
    inline void print(frac a){printf("%lld/%lld\n",a.x,a.y);}
    int n,m,a[N],b[N],c[N],d[M],dep[N+M],now[N+M],s=0,t;
    bool graph[N][M];
    
    inline bool bfs()
    {
    	memset(dep,0,sizeof dep);
    	queue<int> q;	dep[s]=1;
    	q.push(s);	now[s]=head[s];
    	while(!q.empty())
    	{
    		int u=q.front();	q.pop();
    		for(int i=head[u];i;i=e[i].next)
    			if(e[i].val>0)
    			{
    				int v=e[i].to;
    				if(dep[v])	continue;
    				dep[v]=dep[u]+1;
    				now[v]=head[v];
    				if(v==t) return 1;
    				q.push(v);
    			}
    	}
    }
    
    double dinic(int u,double flow)
    {
    	if(u==t)	return flow;
    	double last=flow;
    	for(int &i=now[u];i;i=e[i].next)
    		if(e[i].val>0)
    		{
    			int v=e[i].to;
    			if(dep[v]!=dep[u]+1)	continue;
    			double k=dinic(v,min(last,e[i].val));
    			if(!k)		dep[v]=-1;
    			last-=k;	e[i].val-=k;
    			e[i^1].val+=k;
    			if(!last)	break;
    		}
    }
    
    line solve(double x)
    {
    	initiator();
    	for(int i=1;i<=n;++i)
    		if(b[i]<x)
    		{
    			double lim=a[i]? min((x-b[i])/2/a[i],(double)c[i]):c[i];
    			add(s,i,lim);	add(i,s,0);
    		}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			if(graph[i][j])
    				add(i,n+j,1e18),add(n+j,i,0);
    	for(int i=1;i<=m;++i)
    		add(n+i,t,d[i]),add(t,n+i,0);
    	while(bfs()) dinic(s,1e18);
    	line res;
    	for(int i=2;i<=cnt;i+=2)
    		if(!dep[e[i].to])				//注意这里不能直接判流量(double 永远的痛)
    		{
    			if(e[i].from==s)
    			{
    				int u=e[i].to;
    				if(a[u] && x<2*a[u]*c[u]+b[u])
    				{
    					res.k=res.k+(frac)1/2/a[u];
    					res.b=res.b-(frac)b[u]/2/a[u];
    //					printf("for edge %d->%d k+=%d/%d\n",s,e[i].to,((frac)1/2/a[u]).x,((frac)1/2/a[u]).y);
    				}
    				else res.b=res.b+c[u];//,printf("for edge %d->%d b+=%d\n",s,e[i].to,c[u]);
    			}
    			else if(e[i].to==t && dep[e[i].from])	//说明这条边是流量限制
    				res.b=res.b+d[e[i].from-n];//,printf("for edge %d->%d b+=%d\n",e[i].from,t,d[e[i].from-n]);
    		}
    //	printf("res.k=%d/%d\n",res.k.x,res.k.y);
    }
    
    vector<point> v;
    void solve(line l,line r)
    {
    //	printf("solving (%d/%d)x+(%d/%d) ~ (%d/%d)x+(%d/%d)\n",l.k.x,l.k.y,l.b.x,l.b.y,r.k.x,r.k.y,r.b.x,r.b.y);
    	if(l.k==r.k)	return;
    	frac x=(r.b-l.b)/(l.k-r.k);
    	point p=make_pair(x,x*l.k+l.b);
    	line midl=solve(p.x.value()-eps);
    	line midr=solve(p.x.value()+eps);
    	if(midr==r)	{/*printf("pushing (%d/%d,%d/%d)\n",p.x.x,p.x.y,p.y.x,p.y.y);*/	v.push_back(p); return;}
    	else	solve(l,midl),solve(midr,r);
    }
    
    int main()
    {
    //	freopen("mincost10.in","r",stdin);
    //	freopen("mincost10.out","w",stdout);
    	n=read();	m=read();	t=n+m+1;
    	for(int i=1;i<=n;++i)
    		a[i]=read(),b[i]=read(),c[i]=read();
    	for(int i=1;i<=m;++i)	d[i]=read();
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			graph[i][j]=read();
    	line r=solve(1e9);	frac ans;	assert(r.b.y==1);
    	v.push_back(make_pair(frac(),frac()));
    	for(int i=1;i<=3;++i)
    	{
    		line left=solve(i-1+eps);
    		line right=solve(i-eps);
    		v.push_back(make_pair(frac(i-1),left.k*(i-1)+left.b));
    		solve(left,right);
    		v.push_back(make_pair(frac(i),right.k*i+right.b));
    	}
    	line now=solve(3+eps);
    	v.push_back(make_pair(frac(3),now.k*3+now.b));
    	solve(solve(3+eps),r);
    	for(unsigned i=1;i<v.size();++i)
    	{
    		frac now=(v[i].x+v[i-1].x)*(v[i].y-v[i-1].y)/2;
    //		printf("now ans+=%d/%d\n",now.x,now.y);
    		ans=ans+now;
    	}
    }
    
    • @ 2023-4-7 20:19:00

      删了部分代码,仅提供思路。

  • 1

信息

ID
196
时间
3000ms
内存
256MiB
难度
10
标签
递交数
2
已通过
2
上传者