题目链接
差分约束
这是一道经典的差分约束问题
我们假设最后第i个小朋友分得的糖果数为
ai,ai∈N∗
那么对于约束条件:i分得的糖果少于j的,有
ai<aj
,由于
ai
是整数,可以变形为
ai⩽aj+(−1)
同样,对于
ai⩽aj
也可以看成
ai⩽aj+0
这些条件是不是很像最短路的最终状态中有
dj⩽di+ei,j
呢
于是我们可以将问题转换成最短路/最长路求解
如果存在一个符合要求的最短路(最长路)的解,那么每个点的最短路就是分配的糖果数。而如果因为出现负环没有最短路(出现正环没有最长路),那么原问题无解
如何保障所有的
ai>0
呢?我们新加入一个超级节点s,使得
as=0
,并且新加入n个限制
as<ai,i∈[1,n]
,这样既不影响答案,又能保证性质
最短路?最长路?
不难发现,以上两个限制也可以写成:
这样就符合了最长路的限制 dj⩾di+ei,j
那么最短路和最长路,使用哪一种都可以,没有区别吗?
其实不然,因为 最短路可以最大化 ∑ai ,而最长路可以最小化
为什么呢?因为最短路使得每一个点的权值都是由一条边更新来,而且这个点的权值无法再大,否则就会不符合该边限制。最长路则正好相反,使得所有点权值无法再小。
这题我们要最小化 ∑ai ,故考虑最长路
SPFA做法的缺陷
因为需要判环,考虑通过限制SPFA中节点入队次数来判
每个节点最多入队n次,若环长为n,环上每个节点都会入队O(n)次,总体复杂度
O(N2)
理论上是会T的(虽然大家都是这么写的也没人T,郁闷)
因此我们需要考虑更高效的判环方法
tarjan求SCC+拓扑图DP
考虑什么样的环是正环
显然边权只有两种,正的和0
如果一个环上有一条边是正的,那么整个环就是正环
如果没有,那么整个环的值应当相同
将环的结论拓展到SCC仍然适用
于是我们可以求出所有SCC,若SCC内存在某正边,直接输出-1
若不存在,可将SCC缩为一个点共同处理
缩点之后为一个拓扑图
我们可以每次找入度为0的点到其他点更新最长路,因为这个点已经不能被其他点更新了
至此我们解决了这个问题
那么这个思想可不可以应用到其他查分约束系统呢
答案是未必
只有边权全为非负的最长路和边权全为非正的最短路可以,而一般的差分约束并不保证这一点
代码
#include<stack>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 100006
#define long long long
struct edge
{
int v;
int next;
int w;
edge(int v,int n,int w):v(v),next(n),w(w){}
edge(){}
}e[maxn*3],e2[maxn*3];
int n,m;
int newedge,dfsclock,newcolor;
int ind[maxn],ind2[maxn];
bool flag=true;
void addedge(edge *e,int *ind,int u,int v,int a)
{
//if(e-e2==0)printf("edge:%d %d %d\n",u,v,a);
e[++newedge]=edge(v,ind[u],a);
ind[u]=newedge;
}
long d[maxn];
int dfn[maxn],low[maxn];
int deg[maxn];
int color[maxn];
int s[maxn];
stack<int> stk;
queue<int> que;
void dfs(int u)
{
dfn[u]=low[u]=++dfsclock;
stk.push(u);
for(int i=ind[u];i;i=e[i].next)
{
int v=e[i].v;
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else
if(!color[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
++newcolor;
s[newcolor]=1;
while(stk.top()!=u)
{
color[stk.top()]=newcolor;
s[newcolor]++;
que.push(stk.top());
stk.pop();
}
color[u]=newcolor;
que.push(u);
stk.pop();
while(!que.empty())
{
int t=que.front();
for(int i=ind[t];i;i=e[i].next)
if(color[e[i].v]==newcolor&&e[i].w==1)
flag=false;
que.pop();
}
}
return;
}
long ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
addedge(e,ind,0,i,1);
for(int i=1;i<=m;i++)
{
int x,a,b;
scanf("%d%d%d",&x,&a,&b);
switch(x)
{
case 1:
addedge(e,ind,a,b,0);
addedge(e,ind,b,a,0);
break;
case 2:
addedge(e,ind,a,b,1);
break;
case 3:
addedge(e,ind,b,a,0);
break;
case 4:
addedge(e,ind,b,a,1);
break;
case 5:
addedge(e,ind,a,b,0);
break;
default:puts("QAQ");
}
}
dfs(0);
if(!flag)
{
puts("-1");
return 0;
}
newedge=0;
for(int u=0;u<=n;u++)
for(int i=ind[u];i;i=e[i].next)
{
if(color[u]==color[e[i].v])
continue;
addedge(e2,ind2,color[u],color[e[i].v],e[i].w);
deg[color[e[i].v]]++;
}
que.push(color[0]);
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=ind2[u];i;i=e2[i].next)
{
int v=e2[i].v;
d[v]=max(d[u]+e2[i].w,d[v]);
deg[v]--;
if(!deg[v])
que.push(v);
}
}
for(int i=1;i<=newcolor;i++)
ans+=(long)d[i]*s[i];
printf("%lld",ans);
return 0;
}