7045. 2021.04.05【2021省赛模拟】数学考试(sleep)题解

网络流的好题目
三次函数的形式将我迷惑了,以为是一道数学题
其实这道题的突破口是它奇怪的限制
应该自然联想到网络流!
这里是最小割
不考虑限制,就是对于每个函数每个可能的取值 x ( l ≤ x ≤ r ) x(l\leq x\leq r) x(lxr),从 S S S T T T连一条链,断掉一条边代表你选了 f ( x ) f(x) f(x)作为这个函数的值,容量为 f ( x ) f(x) f(x)
那这样岂不是变成了“最大割”问题?
容量取个相反数不就是最小割?
当然,你会连 r − l + 2 r-l+2 rl+2条边,值却只有 r − l + 1 r-l+1 rl+1个,所以 S S S的出边或 T T T的入边有一条边容量是 i n f inf inf,我将 S S S的出边容量定为 i n f inf inf,那么每个点的出边被断代表选这个点
考虑如何加上限制,其实有个最小割这个思路,限制也不难想。对于一组 ( u , v , d ) (u,v,d) (u,v,d), u x 向 v x − d u_x向v_{x-d} uxvxd连一条容量为 i n f inf inf的边, i n f inf inf不可能被割掉,所以选择了 u x u_x ux就必须选择 v x − d v_{x-d} vxd或以后的边割(选 v x − d v_{x-d} vxd前的边无法改变连通性)
如果 v x − d v_{x-d} vxd没值了怎么办?
这就代表 u x u_x ux根本不能选,因为没有对应的 v x − d v_{x-d} vxd满足限制
T T T连一条 i n f inf inf边即可
然后跑个最小割就能过
实现细节粗节
你会发现,我们的容量有可能是负数!
加上一个大值 C C C使之变成正整数。
答案即为最小割 − n ∗ C -n*C nC(我们知道 1 1 1个函数 1 1 1个取值,也就是一共只会被割 n n n条边)
怎么判无解?
当最小割 ≥ i n f \geq inf inf时,说明你割掉了一条 i n f inf inf边,这是无解的情况
当然 i n f inf inf值要设的足够大

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 700005
#define ll long long
#define C 200000000
#define D 205
#define mem(a) memset(a,0,sizeof(a))
#define S 400001
#define T 400002
#define inf 100000000000
using namespace std;
ll cur[N],gap[N],dis[N],f[N<<1][4],q[N];
ll i,j,k,m,n,o,p,l,s,t,times,k1,k2,k3,k4,last,x,y,d,ans,r;
ll a[105][205],tot;
struct node{
	ll l,r;
}b[10005];
void insert(ll x,ll y,ll z)
{
	if (z!=inf) z=-z+C; 
	f[++t][1]=y,f[t][2]=q[x],f[t][3]=z,q[x]=t;
	f[++t][1]=x,f[t][2]=q[y],f[t][3]=0,q[y]=t;
}
ll dfs(ll t,ll flow)
{
	if (t==T) return flow;
	ll have=0;
	for (ll k=cur[t];k;k=f[k][2])
	{
		if (dis[f[k][1]]+1==dis[t]&&f[k][3])
		{
			cur[t]=k;
			ll now=dfs(f[k][1],min(flow-have,f[k][3]));
			f[k][3]-=now,f[k^1][3]+=now,have+=now;
			if (flow==have) return flow;
		}
	}
	cur[t]=q[t];
	if (!(--gap[dis[t]])) dis[S]=T; 
	++gap[++dis[t]];
	return have;
}
ll cal(ll x) {return k1*x*x*x+k2*x*x+k3*x+k4;}
int main()
{
	freopen("sleep.in","r",stdin);
	freopen("sleep.out","w",stdout);
	scanf("%lld",&times);
	while (times--)
	{
		mem(cur),mem(gap),mem(dis),mem(f),mem(q),t=tot=1;
		scanf("%lld%lld",&n,&m);
		for (i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld%lld%lld%lld",&k1,&k2,&k3,&k4,&l,&r);
			insert(S,++tot,inf);a[i][l+D]=last=tot;
			for (j=l+1,k=2;j<=r;j++,k++)
			{
				insert(last,++tot,cal(j-1));
				a[i][j+D]=last=tot;
			}
			insert(tot,T,cal(r));
			b[i]=(node){l,r};
		}p=0;
		for (i=1;i<=m;i++)
		{
			scanf("%lld%lld%lld",&x,&y,&d);
			o=1e9;
			for (j=b[x].l;j<=b[x].r;j++)
			{
				if (j-d<=b[y].r)
				{
					o=1;
				}
				if (j-d>=b[y].l&&j-d<=b[y].r)
				{
					insert(a[x][j+D],a[y][j-d+D],inf);
				}
				if (j-d>=b[y].r+1) insert(a[x][j+D],T,inf);
			}
			if (o==1e9) 
			{
				p=1;//break;
			}
		}
/*		if (p)
		{
			puts("mei ji ge");
			continue;
		}*/
		ans=0;
		while (dis[S]<T) 
			ans+=dfs(S,inf);
		if (ans>=inf)
		{
			puts("mei ji ge");
			continue;
		}
		printf("%lld\n",n*C-ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值