1. 程式人生 > >[BZOJ2330] [SCOI2011] 糖果 [差分約束][單源最短路][縮點][拓撲排序]

[BZOJ2330] [SCOI2011] 糖果 [差分約束][單源最短路][縮點][拓撲排序]

link

SPFA

題目要求求最小值。
建原點0\mathfrak{0},也就是要dis[x]dis[0]\mathfrak{\sum{dis[x]-dis[0]}} 最小。
最小值受到dis[x]dis[0]val[x][0]\mathfrak{dis[x]-dis[0]\ge val[x][0]}的約束也即dis[0]+val[x][0]dis[x]\mathfrak{dis[0]+val[x][0]\le dis[x]}
連邊跑差分約束,最長路。
然後最長路就dijkstra\mathfrak{dijkstra}

用不了了,不過資料範圍也不允許BellmanFord\mathfrak{Bellman-Ford}
總之就用spfa\mathfrak{spfa}期望過一個吧(

一定有約束,所以不用判連通。不過要判負環(輸出"1"\mathfrak{"-1"}的情況)

聽說這題甚至構造資料試圖卡spfa\mathfrak{spfa}?有點恐怖的,難道還能dijkstra\mathfrak{dijkstra}
(不過好像也可以縮點+拓撲排序的方式來做)
總之s

lf+lfu\mathfrak{slf}+\mathfrak{lfu}大法好

#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; } 
縮點+拓撲排序

spfa\mathfrak{spfa}的複雜度上界是Θ(NM)\mathcal{\Theta(NM)},所以spfa\mathfrak{spfa}跑差分約束的做法其實不是正解。

圖中只有五種約束關係。
如果關係不成環,那就很好做了;如果關係成環呢?
1\mathfrak{1}代表兩個點一樣,可以縮成一個。
關係2,4\mathfrak{2,4}是差不多的,如果靠這兩種關係成環,環裡面的點值都相同,也可以縮點。

3,5\mathfrak{3,5}成環無解。
所以先縮點,然後用剩下的2,4\mathfrak{2,4}3,5\mathfrak{3,5}分別統計。

#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(<