BZOJ2330: [SCOI2011]糖果

显然可以差分约束,但是n,m<=1e5,且有两个数据如下:

  1. 1->2->...->n的一条链。若1先入队,则可以一次更新完。否则每次编号较小的点会把所有编号大于它的点都重新更新一次,就卡到了o(n^2)。若一般的边表按1->n加边,入队顺序就是n->1。面向数据地,可以倒着加边,或者按1->n先把所有点入队。
  2. 有负环的大数据。由于spfa判负环是O(nm)的,所以这个点要跑5s。然而这个点存在负的自环,可以直接判掉……

有一个tarjan缩点,拓扑排序的做法,好像没有上述面向数据的问题。

#include<bits/stdc++.h>
const int N=1e5+5;
using namespace std;
int n,m,k,s,t;
struct edge{
	int v,w;edge*s;
}e[N*3];
edge*b=e,*h[N];
void add(int u,int v,int w){
	edge s={v,w,h[u]};
	*(h[u]=b++)=s;
}
int d[N],a[N],z[N],f[N];
bool spfa(){
	deque<int>q(1);
	while(q.size()){
		int u=q.front();
		q.pop_front();
		z[u]=0;
		for(edge*i=h[u];i;i=i->s)
			if(d[u]+i->w>d[i->v]){
				d[i->v]=d[u]+i->w;
				if(!z[i->v]++){
					if(++a[i->v]==n)
						return 0;
					if(q.size()&&d[i->v]<d[q.front()])
						q.push_back(i->v);
					else
						q.push_front(i->v);
				}
			}
	}
	return 1;
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d%d",&k,&s,&t);
		if(k==1){
			add(s,t,0);
			add(t,s,0);
		}
		if(k==2){
			if(s==t)return!~puts("-1");
			add(s,t,1);
		}
		if(k==3)add(t,s,0);
		if(k==4){
			if(s==t)return!~puts("-1");
			add(t,s,1);
		}
		if(k==5)add(s,t,0);
	}
	for(int i=1;i<=n;++i)
		f[i]=i;
	random_shuffle(f+1,f+n+1);
	for(int i=1;i<=n;++i)
		add(0,f[i],1);
	printf("%lld\n",!spfa()?-1:accumulate(d+1,d+n+1,0ll));
}
posted @ 2016-07-30 17:50  f321dd  阅读(304)  评论(0编辑  收藏  举报