差分约数的

        满足多组二元差的不等式关系x1...xn,求是否有解,最小解,最大解。求解一般用spfa(因为有负权)。对于最小解一般用最长路,最大解一般用最短路。对于小于号和大于号,可以通过减1来变成小于等于或者大于等于。不满足需要的不等式的时候可以给k添个负号。

        对于x-y<=k,k是边权,x,y是点,可以转化成x<=y+k。我们令有k的一边表示起始点,y+k就是起始点的距离加上起始点到终点的边权,那么x<=y+k就是让终点的dis要小于y+k,也就是说松弛条件就是if(dis【x】>dis【y】+w)就更新dis【x】,使得x<=y+k。可以发现这是最短路的松弛。由于我们每次都是令x取得x<=y+k满足条件的最大值(y+k),所以最终结果就是满足条件的最大值。

        对于x-y>=k,可以转化成x>=y+k,也就是起点的距离加上边权要小于终点的距离,松弛条件就是if(dis【x】<dis【y】+k)就更新dis【x】,这样使得x>=y+k。可以发现这是最长路的松弛。由于我们每次都是令x取得x>=y+k满足条件的最小值(y+k),所以最终结果就是满足条件的最小值。

题目链接

        由于题目要求最小值,我们要用最长路算法,那么关系就要找x+y>=k。对于条件2,A要比B小,也就是A<B,A+1<=B,也就是B>=A+1,那么起点就是A,边权就是1。对于条件4,A>B,A>=B+1,那么起点就是B,边权就是1。因为条件1是相等,那么我们只需要用两条不等式的交集来表示范围即可,A>=B+0,B>=A+0,这两条不等式的交集条件就是A=B。

        代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

queue<int>q;

struct edge
{
	int to,from,v;
}e[800010];

int n,dis[400010],head[400010],cnt=1,bz[400010],k;
bool f[400010];

void insert(int x,int y,int v)
{
	e[++cnt].to=y;e[cnt].from=head[x];e[cnt].v=v;head[x]=cnt;
}

int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=k;i++)
	{
		int x,a,b;
		scanf("%d%d%d",&x,&a,&b);
		if (x==1)
		{
			insert(a,b,0);
			insert(b,a,0);
		} else
		if (x==2)
		{
			if (a==b)
			{
				printf("-1");
				return 0;
			}
			insert(a,b,1);
		} else
		if (x==3) insert(b,a,0); else
		if (x==4)
		{
			if (a==b)
			{
				printf("-1");
				return 0;
			}
			insert(b,a,1);
		} else
		if (x==5) insert(a,b,0);
	}
	for (int i=n;i>=1;i--)
		insert(0,i,1);
	memset(dis,0,sizeof(dis));
	memset(f,false,sizeof(f));
    f[0]=true;
	q.push(0);
    while (!q.empty())
    {
        int u=q.front();
		q.pop();
		f[u]=false;
		if (bz[u]==n-1)
		{
			printf("-1");
			return 0;
		}
		bz[u]++;
        for (int i=head[u];i;i=e[i].from)
            if (dis[e[i].to]<dis[u]+e[i].v)
            {
                dis[e[i].to]=dis[u]+e[i].v;
                if (!f[e[i].to])
                {
                    f[e[i].to]=true;
                    q.push(e[i].to);
                }
            }
    }
    long long ans=0;
    for (int i=1;i<=n;i++)
    	ans+=dis[i];
    printf("%lld",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值