题解 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;
}

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

猜你喜欢

转载自www.cnblogs.com/suxxsfe/p/12527397.html
今日推荐