【差分约束系统+单调栈】51Nod1340 地铁环线

【题目】
51Nod
有一幅 n n n个点的环图,其中第 i i i个点和第 ( i + 1 )  mod  n (i+1)\text{ mod }n (i+1) mod n之间有一条有向带权边(至少为 1 1 1)。
m 1 + m 2 m_1+m_2 m1+m2条信息限制两点之间的距离大于或小于某个值。
求有多少种不同的可能环长,若有无数种输出 − 1 -1 1

【解题思路】
差分约束系统,不会做。

不妨先考虑当总长度 s s s固定时如何判断有解,这是一个经典问题:
对于限制 d i s ( u , v ) ≥ d dis(u,v)\geq d dis(u,v)d,若 u &lt; v u&lt;v u<v,连边 ( v , u , − d ) (v,u,-d) (v,u,d),否则连边 ( u , v , s − d ) (u,v,s-d) (u,v,sd)
对于限制 d i s ( u , v ) ≤ d dis(u,v)\leq d dis(u,v)d,若 u &lt; v u&lt;v u<v,连边 ( u , v , d ) (u,v,d) (u,v,d),否则连边 ( u , v , − s + d ) (u,v,-s+d) (u,v,s+d)
然后判断是否有负环即可。

现在要求方案数,然而我并不会。

发现每条边的边权是一个一次函数,进行 n − 1 n-1 n1次松弛操作,于是对于每个点的每种 s s s的系数k记录 d i s x , k dis_{x,k} disx,k,表示当走到 x x x时, s s s的系数为 k k k的最小值为 d i s x , k dis_{x,k} disx,k

对于每一条边 ( u , v ) (u,v) (u,v)维护一个单调栈,表示每一段 s s s的值域是由 s s s的哪个系数控制

对于一段某一段 s s s的值域的,代入 s s s,如果 u u u点的函数值 + ( u , v ) &gt; b +(u,v)&gt;b +(u,v)>b点的函数值,则这一段值域对于这一条边是合法的。用个前缀和数组,这一段值域的合法边数加 1 1 1.

当一段值域的合法边数为总边数时,这段值域的 s s s都合法, a n s ans ans加上个数。

每组数据复杂度 O ( n 2 m ) O(n^2m) O(n2m)

【参考代码】(抄的)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=55,M=N*N*N*2,mod=1e9+7;
int T,n,m1,m2,tot,num;
ll inf,dis[N][N<<1];

ll read()
{
	ll ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct lis
{
	ll x,v;
	lis(ll _x=0,ll _v=0):x(_x),v(_v){}
	bool operator <(const lis&rhs){return x<rhs.x;}
}vec[M];

struct line
{
	ll l,r,k,b;
	line(ll _l=0,ll _r=0,ll _k=0,ll _b=0):l(_l),r(_r),k(_k),b(_b){}
	ll f(ll x){return k*x+b;}
}s1[M],t1[M];

ll up(ll b,ll k)
{
	if(k<0) k=-k,b=-b;
	ll t=b/k; return t+(t*k<b);
}
ll down(ll b,ll k)
{
	if(k<0) k=-k,b=-b;
	ll t=b/k; return t-(t*k>b);
}
void push(line *a,int &cnt,line t)
{
	while(cnt && t.f(a[cnt].l)<=a[cnt].f(a[cnt].l)) --cnt;
	if(cnt) t.l=down(-(a[cnt].b-t.b),a[cnt].k-t.k)+1,a[cnt].r=t.l-1;
	a[++cnt]=t;
}
void insert(ll l,ll r)
{
	vec[++num]=lis(l,1);
	vec[++num]=lis(r+1,-1);
}
void check(line s1,line t1)
{
	ll l=max(s1.l,t1.l),r=min(s1.r,t1.r);
	if(l>r) return;
	line t=line(0,0,s1.k-t1.k,s1.b-t1.b);
	if(t.f(l)<0 && t.f(r)<0) return;
	insert(t.f(l)>=0?l:up(-t.b,t.k),t.f(r)>=0?r:down(-t.b,t.k));
}

struct edge
{
	ll x,y,v,k;
	edge(ll _x=0,ll _y=0,ll _v=0,ll _k=0):x(_x),y(_y),v(_v),k(_k){}
	void brute()
	{
		for(int i=1;i<2*n;++i)
			if(i+k>=0 && i+k<=2*n) dis[y][i+k]=min(dis[y][i+k],dis[x][i]+v);
	}
	void calc()
	{
		int a=0,b=0;
		for(int i=2*n;i;--i)
		{
			if(dis[x][i]<inf/2) push(s1,a,line(n,inf,i+k-n,dis[x][i]+v));
			if(dis[y][i]<inf/2) push(t1,b,line(n,inf,i-n,dis[y][i]));
		}
		for(int k1=1,k2=1;k1<=a && k2<=b;s1[k1].r<=t1[k2].r?k1++:k2++) check(s1[k1],t1[k2]);
	}
}e[N<<2];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("51Nod1340.in","r",stdin);
	freopen("51Nod1340.out","w",stdout);
#endif
	for(int T=read();T--;)
	{
		n=read();m1=read();m2=read();memset(dis,1,sizeof(dis));memset(vec,0,sizeof(vec));
		inf=dis[0][0];dis[0][n]=0;tot=0;
		for(int i=0;i<n;++i) e[++tot]=edge((i+1)%n,i,-1,i==n-1);
		for(int i=1;i<=m1;++i)
		{
			ll x=read(),y=read(),w=read();
			e[++tot]=edge(y,x,-w,(x>y));
		}
		for(int i=1;i<=m2;++i)
		{
			ll x=read(),y=read(),w=read();
			e[++tot]=edge(x,y,w,-(x>y));
		}
		//for(int i=1;i<=tot;++i) printf("%lld %lld %lld %lld\n",e[i].x,e[i].y,e[i].v,e[i].k);
		for(int i=1;i<n;++i) for(int j=1;j<=tot;++j) e[j].brute();
		for(int i=1;i<=tot;++i) e[i].calc();
		sort(vec+1,vec+num+1);vec[num+1]=lis(inf+1,0);
		ll sum=0,ans=0;
		//for(int i=1;i<=num;++i) printf("%lld %lld\n",vec[i].x,vec[i].v);
		for(int i=1;i<=num;++i) ans+=((sum+=vec[i].v)==tot)*(vec[i+1].x-vec[i].x);
		printf("%lld\n",ans>=inf/4?-1:ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值