suxxsfe

一言(ヒトコト)

题解 P2879 【[USACO07JAN]区间统计Tallest Cow】

P2879 【[USACO07JAN]区间统计Tallest Cow】

其实我是刚学完哈希用标签搜哈希进来的,但打开一看觉得是个图论....
看了题解才知道,确实和哈希没啥关系其实就是用map判重,然而我还是建图跑的拓扑
看题解区只有一篇类似的做法还不是特详细,就赶紧来水一篇


有n只牛,给你r条信息和最高的牛的高度,每条信息\((a,b)\)表示\(h_a\leq h_b\),且a,b之间所有牛高度比\(h_a\)低,求每头牛最大高度
建图时从高的牛连向低的牛,小于的情况边权为1(高度至少要低1)
对于小于等于的情况当然要让它取等(因为要求最大高度),所以边权应为0
拓扑时,对于边\((u,v)\),让\(h[v]=min(h[v],h[u]-w[i])\),其中\(w[i]\)是当前边边权
然后发现这样建图边数是\(Rn\)的,所以考虑用线段树优化
在线段树中的点是从\(n+1\)开始编号,对应我代码里的nn
最后注意线段树中的边边权也要为0就行了

#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,nn,maxh,m;
int h[30006],fir[30006],in[30006];
int nex[200006],to[200006],w[200006],edge;
struct tr{
	tr *ls,*rs;
	int id;
}dizhi[20006],*root=&dizhi[0];
int tot;
inline void add(int u,int v,int tmp){
	to[++edge]=v;w[edge]=tmp;
	nex[edge]=fir[u];fir[u]=edge;
}
void build(tr *tree,int l,int r){
	if(l==r) return tree->id=l,void();
	int mid=(l+r)>>1;
	tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
	build(tree->ls,l,mid);build(tree->rs,mid+1,r);
	tree->id=++nn;
	add(tree->id,tree->ls->id,0);add(tree->id,tree->rs->id,0);
	in[tree->ls->id]++;in[tree->rs->id]++;
}
void addtree(tr *tree,int l,int r,int ql,int qr,int u){
	if(ql<=l&&r<=qr) return add(u,tree->id,1),in[tree->id]++,void();
	int mid=(l+r)>>1;
	if(ql<=mid) addtree(tree->ls,l,mid,ql,qr,u);
	if(qr>mid) addtree(tree->rs,mid+1,r,ql,qr,u);
}
std::queue<int>q;
inline void topo(){
	for(reg int i=1;i<=nn;i++){
		h[i]=maxh;
		if(!in[i]) q.push(i);
	}
	while(!q.empty()){
		reg int u=q.front();q.pop();
		for(reg int v,i=fir[u];i;i=nex[i]){
			v=to[i];
			h[v]=std::min(h[v],h[u]-w[i]);
			if(!--in[v]) q.push(v);
		}
	}
}
int main(){
	n=nn=read();read();maxh=read();m=read();//那个输入的i并没有什么用,直接read()掉就行,代码里的m就是题里的R
	reg int a,b;
	build(root,1,n);
	while(m--){
		a=read();b=read();
		add(b,a,0);in[a]++;
		reg int minn=std::min(a,b),maxx=std::max(a,b);
		if(minn<maxx-1) addtree(root,1,n,minn+1,maxx-1,a);//a,b之间至少有一头牛
	}
	topo();
	for(reg int i=1;i<=n;i++) std::printf("%d\n",h[i]);
	return 0;
}

还有一个相似的题可以去写一下
题解
那个题是求一种可行方案,但其实就可以当最大值做

posted @ 2020-03-19 21:37  suxxsfe  阅读(122)  评论(0编辑  收藏  举报