[BZOJ2330] [SCOI2011] 糖果 [差分約束][單源最短路][縮點][拓撲排序]
阿新 • • 發佈:2019-02-02
SPFA
題目要求求最小值。
建原點,也就是要 最小。
最小值受到的約束也即
連邊跑差分約束,最長路。
然後最長路就用不了了,不過資料範圍也不允許
總之就用期望過一個吧(
一定有約束,所以不用判連通。不過要判負環(輸出的情況)
聽說這題甚至構造資料試圖卡?有點恐怖的,難道還能
(不過好像也可以縮點+拓撲排序的方式來做)
總之大法好
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<cctype>
#include<cmath>
#include<ctime>
#include<queue>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
#define ll long long
char frBB[1<<12],*frS=frBB,*frT=frBB;
inline void read(int&T)
{
int x=0;char ch=getchar();bool w=0;
while(!isdigit(ch))w|=(ch=='-'),ch=getchar();
while(isdigit(ch))x=x*10+(ch-48),ch=getchar();
T=w?-x:x;
}
inline void write(ll T)
{
if(T<0)putchar('-'),T=-T;
if(T>9)write(T/10);
putchar(T%10+48);
}
#define add_edge(a,b,c) nxt[++tot]=head[a],head[a]=tot,to[tot]=b,val[tot]=c,slf-=c
int limR,limL,slf;
int N,K,tot=0;
ll ans=0;
int nxt[300005]={},to[300005]={},head[100005]={},val[300005]={};
int dis[100005]={},ext[100005]={},vt[100005]={};
bool vis[100005]={};
deque<int>Q;
bool spfa()
{
memset(dis,0x3f,sizeof(dis));
Q.push_back(0); dis[0]=0; ext[0]=0; vt[0]=1;
int cnt=0;
while(!Q.empty())
{
int x=Q.front(); vis[x]=0; Q.pop_front(); ++cnt;
for(int i=head[x];i;i=nxt[i])
{
if(dis[to[i]]>dis[x]+val[i])
{
dis[to[i]]=dis[x]+val[i];
if(!vis[to[i]])
{
vis[to[i]]=1; ++vt[to[i]];
if((((!Q.empty())&&dis[to[i]]>dis[Q.front()]+slf))||(vt[to[i]]<=limR&&vt[to[i]]>=limL))Q.push_back(to[i]);
else Q.push_front(to[i]);
ext[to[i]]=ext[x]+1;
if(ext[to[i]]>N)return 1;
}
}
}
}
return 0;
}
int main()
{
limL=2; limR=sqrt(N);
read(N); read(K);
for(int X,A,B,i=1;i<=K;++i)
{
read(X); read(A); read(B);
switch(X)
{
case 1:
add_edge(A,B,0),add_edge(B,A,0);
break;
case 2:
if(A==B)
{
printf("-1");
return 0;
}
add_edge(A,B,-1);
break;
case 3:
add_edge(B,A,0);
break;
case 4:
if(A==B)
{
printf("-1");
return 0;
}
add_edge(B,A,-1);
break;
case 5:
add_edge(A,B,0);
break;
}
}
for(int i=1;i<=N;++i)add_edge(0,i,-1);
slf=sqrt(slf);
if(spfa())
{
puts("-1");
return 0;
}
for(int i=1;i<=N;++i)ans+=dis[i];
write(-ans);
return 0;
}
縮點+拓撲排序
的複雜度上界是,所以跑差分約束的做法其實不是正解。
圖中只有五種約束關係。
如果關係不成環,那就很好做了;如果關係成環呢?
代表兩個點一樣,可以縮成一個。
關係是差不多的,如果靠這兩種關係成環,環裡面的點值都相同,也可以縮點。
而成環無解。
所以先縮點,然後用剩下的和分別統計。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<cctype>
#include<cmath>
#include<ctime>
#include<queue>
using namespace std;
#define getchar() (frS==frT&&(frT=(frS=frBB)+fread(frBB,1,1<<12,stdin),frS==frT)?EOF:*frS++)
#define ll long long
char frBB[1<<12],*frS=frBB,*frT=frBB;
inline void read(int&T)
{
int x=0;char ch=getchar();bool w=0;
while(!isdigit(ch))w|=(ch=='-'),ch=getchar();
while(isdigit(ch))x=x*10+(ch-48),ch=getchar();
T=w?-x:x;
}
inline void write(ll T)
{
if(T<0)putchar('-'),T=-T;
if(T>9)write(T/10);
putchar(T%10+48);
}
#define add_edge(a,b) nxt[++tot]=head[a],head[a]=tot,to[tot]=b
#define readd_edge(a,b,c) bnxt[++tot]=bhead[a],bhead[a]=tot,bto[tot]=b,type[tot]=c,++in[b]
int N,K,tot=0;
long long ans=0;
int head[100005]={},to[200005]={},nxt[200005]={},dfn[100005]={},low[100005]={};
int dfs_id=0;
int stk[100005]={},col[100005]={},siz[100005]={};
int bhead[100005]={},bto[200005]={},bnxt[200005]={};
bool type[200005]={};
struct adt
{
int x,u,v;
adt(int a=0,int b=0,int c=0)
{
x=a; u=b; v=c;
}
}E[200005];
void tarjan(int x)
{
dfn[x]=low[x]=++dfs_id; stk[++stk[0]]=x;
for(int i=head[x];i;i=nxt[i])
{
if(!dfn[to[i]])
{
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(!col[to[i]])
{
low[x]=min(low[x],dfn[to[i]]);
}
}
if(low[x]==dfn[x])
{
++col[0];
while(stk[0])
{
col[stk[stk[0]]]=col[0];
++siz[col[0]];
if(stk[stk[0]--]==x)break;
}
}
}
queue<int>Q;
int in[100005]={};
int an[100005]={};
void topo_sort()
{
for(int i=1;i<=col[0];++i)if(!in[i])Q.push(i),an[i]=1;
while(!Q.empty())
{
int x=Q.front(); Q.pop();
for(int i=bhead[x];i;i=bnxt[i])
{
--in[bto[i]];
if(<