洛谷 P3275 [SCOI2011] 糖果
题目链接: P3275 [SCOI2011] 糖果
题目大意:
有 \(N\) 个大于 \(0\) 的数值,其中有 \(M\) 对之间的关系已经给出,请求出所有数字和至少有多大,若无解则输出 \(-1\) 。
关系有 \(a=b\) ,\(a>b\) ,\(a<b\) , \(a\geq b\) , \(a\leq b\) 五种。
\(N,M\leq 10^5\)
思路:
这道题外表看起来是一个差分约束板子(如果你是卡常大师的话那确实如此),不过出题人没有那么良心,他卡了 \(SPFA\) 。
由于每个数字 \(\geq 1\) ,可以设 \(d[0]=0\) ,让 \(\forall x,d[x]-d[0]\geq 1\) ,建完图后,考虑这题有什么特殊性质。
连边的时候可以发现所有的边权都是 \(\geq 0\) 的,这意味着如果图中出现一个环,则环上的边权必须全为 \(0\) ,否则必然无解。在这个思路引导下,用 \(Tarjan\) 求出图中的所有强连通分量,而 \(SCC\) 在这里的性质与环相同,若有非零边则无解,否则分量中的所有数都应该相等,这时我们可以将其视为一整块,进行缩点,然后便得到了一张 \(DAG\) ,这样事情就简单了,从节点 \(0\) 所在的 \(SCC\) 出发进行拓扑即可得到每个块的最小数值。
时间复杂度 \(O(N+M)\)
实现细节:
- \(ans\) 开 \(long\) \(long\)
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#define N 100100
#define M 300100
using namespace std;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int head[N],to[M],nxt[M],len[M];
int H[N],T[M],NX[M],L[M];
int dfn[N],low[N];
int c[N],siz[N],deg[N],dis[N];
int cnt,num,scc;
bool in[N];
stack<int> s;
queue<int> q;
void init(){
memset(head,-1,sizeof(head));
memset(H,-1,sizeof(H));
cnt=-1;
}
void add_e(int a,int b,int l){
nxt[++cnt]=head[a],head[a]=cnt,to[cnt]=b,len[cnt]=l;
}
void add_c(int a,int b,int l){
NX[++cnt]=H[a],H[a]=cnt,T[cnt]=b,L[cnt]=l;
}
void tarjan(int x){
dfn[x]=low[x]=++num;
s.push(x),in[x]=true;
for(int i=head[x];~i;i=nxt[i]){
int y=to[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}else if(in[y])
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
scc++; int y;
do{
y=s.top(),s.pop();
in[y]=false,c[y]=scc;
siz[scc]++;
}while(x!=y);
}
}
void topo(){
q.push(c[0]);
while(!q.empty()){
int cur=q.front(); q.pop();
for(int i=H[cur];~i;i=NX[i]){
dis[T[i]]=max(dis[T[i]],dis[cur]+L[i]);
if(--deg[T[i]]==0)q.push(T[i]);
}
}
}
int main(){
int n,k,x,a,b;
cin>>n>>k;
init();
for(int i=0;i<k;i++){
x=read(),a=read(),b=read();
switch(x){
case 1:add_e(a,b,0),add_e(b,a,0); break;
case 2:add_e(a,b,1); break;
case 3:add_e(b,a,0); break;
case 4:add_e(b,a,1); break;
case 5:add_e(a,b,0);
}
}
for(int i=1;i<=n;i++)add_e(0,i,1);
tarjan(0);
cnt=-1;
for(int i=0;i<=n;i++){
for(int j=head[i];~j;j=nxt[j]){
if(c[i]!=c[to[j]])
add_c(c[i],c[to[j]],len[j]),deg[c[to[j]]]++;
else if(len[j])return puts("-1"),0;
}
}
topo();
long long ans=0;
for(int i=1;i<=scc;i++)ans+=(long long)siz[i]*dis[i];
cout<<ans;
return 0;
}