SCOI2011 糖果
比较裸的差分约束……
- 当\(k=1\),在\(a,b\)之间连一条边权为\(0\)的双向边
- 当\(k=2\),从\(a\)向\(b\)连一条边权为\(1\)的边
- 当\(k=3\),从\(b\)向\(a\)连一条边权为\(0\)的边
- 当\(k=4\),从\(b\)向\(a\)连一条边权为\(1\)的边
- 当\(k=5\),从\(a\)向\(b\)连一条边权为\(0\)的边
这里还有两个小剪枝,一个是如果\(k=2\)或\(k=4\)时,\(a=b\),那么肯定无解;另一个是从\(0\)号虚拟节点向其他各节点连边时要反向循环,不然会\(\tt{T}\)一个点,具体原因不明……
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read(){
int k=0; char c=getchar();
for(;c<'0'||c>'9';) c=getchar();
for(;c>='0'&&c<='9';c=getchar())
k=k*10+c-48;
return k;
}
struct zzz{
int t,len,nex;
}e[200010<<1]; int head[100010],tot;
void add(int x,int y,int z){
e[++tot].t=y; e[tot].len=z;
e[tot].nex=head[x]; head[x]=tot;
}
int dis[100010],inq[100010];
bool vis[100010];
long long ans;
int main(){
int n=read(),k=read();
for(int i=1;i<=k;i++){
int k=read(),x=read(),y=read();
if(k==1) add(x,y,0), add(y,x,0);
if(k==2){
if(x==y){ //剪枝1
cout<<-1; return 0;
}
add(x,y,1);
}
if(k==3) add(y,x,0);
if(k==4){
if(x==y){ //剪枝1
cout<<-1; return 0;
}
add(y,x,1);
}
if(k==5) add(x,y,0);
}
for(int i=n;i>=1;i--) add(0,i,1); //剪枝2
memset(dis,1277,sizeof(dis));
queue <int> q; q.push(0); vis[0]=1; dis[0]=0;
while(!q.empty()){
int k=q.front(); q.pop(); vis[k]=0;
for(int i=head[k];i;i=e[i].nex){
if(dis[e[i].t]<dis[k]+e[i].len){
dis[e[i].t]=dis[k]+e[i].len;
if(!vis[e[i].t]){
if(inq[e[i].t]==n-1){
cout<<-1; return 0;
}
inq[e[i].t]++;
q.push(e[i].t), vis[e[i].t]=1;
}
}
}
}
for(int i=1;i<=n;i++) ans+=dis[i];
cout<<ans;
return 0;
}