hdu 5764 After a Sleepless Night(2016多校第四场1002) 线段树

显然对于值为n的点一定构成链,然后我们只需要把两端最小的点作为根(为了字典序),然后就好办了,对于每棵子树根一定是最大值,然后最大值也一定形成链,这样我们就能把所有能确定的点确定下来(根的底端),然后不能确定的点的值一定小于等于输入的全值,下面介绍个人的处理方法:

我们从没有分配的最小的权值开始,因为字典序所以要分配给序号小的点,那么怎么办,我们可以先把所有未分配的店按输入的权值从小到大排序,对于每个点看看输入权值之前能分配多少个权值,如果分配个数大于这个点之前没有分配的点的数量,那么就能从这个点的后面取点扔到这个点的前面,用线段树维护即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int maxn=100005;
vector<int>g[maxn];
int vis[maxn];
int use[maxn];
int a[maxn];
int in[maxn];
int ans[maxn];
int s[maxn];
int fa[maxn];
int find(int pa){
    if(fa[pa]==-1||fa[pa]==pa) return fa[pa]=pa;
    return fa[pa]=find(fa[pa]);
}
void init(int n){
    for(int i=0;i<=n;i++){
        vis[i]=0;
        in[i]=0;
        fa[i]=-1;
        use[i]=0;
        ans[i]=0;
        g[i].clear();
    }
}
int flag;
void dfs(int u,int pa){
    int cnt=0;
    for(int v:g[u]){
        if(v!=pa){
            if(a[v]>a[u]){
                flag=1;
                return;
            }
            if(a[v]==a[u]){
                cnt++;
            }
        }
    }
    if(cnt>1){
        flag=1;
        return;
    }
    if(cnt==0){
        ans[u]=a[u];
        if(use[a[u]]){
            flag=1;
            return;
        }
        use[a[u]]=1;
    }
    for(int v:g[u]){
        if(v!=pa)
            dfs(v,u);
    }
}
int x[maxn],y[maxn],to[maxn];
int cmp(int x,int y){
    if(a[x]!=a[y]) return a[x]<a[y];
    return x<y;
}
struct pi{
    int le,ri;
    int m1;
    int m2;
    int lazy;
}pp[maxn<<2];
void build(int tot,int l,int r){
    pp[tot].le=l;
    pp[tot].ri=r;
    pp[tot].lazy=0;
    if(l==r){
        pp[tot].m1=x[l];
        pp[tot].m2=s[a[x[l]]]-l;
        return;
    }
    build(2*tot,l,(l+r)/2);
    build(2*tot+1,(l+r)/2+1,r);
    pp[tot].m1=min(pp[2*tot].m1,pp[2*tot+1].m1);
    pp[tot].m2=min(pp[2*tot].m2,pp[2*tot+1].m2);
}
void merg1(int tot,int x){
    if(pp[tot].le==pp[tot].ri){
        pp[tot].m1=1000000000;
        return;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(x<=mid) merg1(2*tot,x);
    else merg1(2*tot+1,x);
    pp[tot].m1=min(pp[2*tot].m1,pp[2*tot+1].m1);
}
void merg2(int tot,int l,int r,int la){
    if(l>r) return;
    if(pp[tot].le>=l&&pp[tot].ri<=r){
        pp[tot].lazy+=la;
        return;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(l<=mid) merg2(2*tot,l,r,la);
    if(r>mid) merg2(2*tot+1,l,r,la);
    pp[tot].m2=min(pp[2*tot].m2+pp[2*tot].lazy,pp[2*tot+1].m2+pp[2*tot+1].lazy);
}
int query(int tot,int all){
    if(all+pp[tot].lazy+pp[tot].m2>0) return pp[tot].ri+1;
    if(pp[tot].le==pp[tot].ri) return pp[tot].le;
    all+=pp[tot].lazy;
    int s= query(2*tot,all);
    if(s<=pp[2*tot].ri) return s;
    return query(2*tot+1,all);
}
int query1(int tot,int l,int r){
    if(pp[tot].le>=l&&pp[tot].ri<=r) return pp[tot].m1;
    int s=1000000000;
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(l<=mid) s=min(s,query1(2*tot,l,r));
    if(r>mid) s=min(s,query1(2*tot+1,l,r));
    return s;
}
int main()
{
    int t,N=0;
    cin>>t;
    while(t--){
        int n;
        scanf("%d",&n);
        init(n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }
        for(int i=1;i<=n;i++){
            if(a[i]==n){
                vis[i]=1;
            }
        }
        for(int i=1;i<=n;i++){
            if(!vis[i]) continue;
            for(int v:g[i]){
                if(vis[v]){
                    fa[find(i)]=find(v);
                    if(i<v){
                        in[i]++;
                        in[v]++;
                    }
                }
            }
        }
        int all=0;
        flag=0;
        for(int i=1;i<=n;i++){
            if(vis[i]){
                if(all==0){
                    all=find(i);
                }
                else{
                    if(all!=find(i)){
                        flag=1;
                    }
                }
            }
        }
        if(all==0) flag=1;
        if(flag){
            printf("Case #%d: Impossible\n",++N);
            continue;
        }
        for(int i=1;i<=n;i++){
            if(vis[i]){
                if(in[i]>2){
                    flag=1;
                }
            }
        }
        if(flag){
            printf("Case #%d: Impossible\n",++N);
            continue;
        }
        int root=0;
        for(int i=1;i<=n;i++){
            if(vis[i]){
                if(in[i]==1||in[i]==0){
                    root=i;
                    break;
                }
            }
        }
        dfs(root,-1);
        if(flag){
            printf("Case #%d: Impossible\n",++N);
            continue;
        }
        int tot=0;
        for(int i=1;i<=n;i++){
            if(ans[i]==0) x[++tot]=i;
        }
        if(tot==0){
            printf("Case #%d:",++N);
            for(int i=1;i<=n;i++) printf(" %d",ans[i]);
            printf("\n");
            continue;
        }
        sort(x+1,x+tot+1,cmp);
        tot=0;
        for(int i=1;i<=n;i++){
            s[i]=s[i-1];
            if(use[i]==0){ y[++tot]=i;
                s[i]++;
            }
        }
        for(int i=1;i<=tot;i++){
            if(s[a[x[i]]]<i){
                flag=1;
                break;
            }
        }
        if(flag){
            printf("Case #%d: Impossible\n",++N);
            continue;
        }
        for(int i=1;i<=tot;i++) to[x[i]]=i;
        build(1,1,tot);
        for(int i=1;i<=tot;i++){
            int w=query(1,0);
            if(w>tot){
                w=query1(1,1,tot);
                ans[w]=y[i];
            }
            else{
                int w1=query1(1,1,w);
                ans[w1]=y[i];
                w=w1;
            }
            merg1(1,to[w]);
            merg2(1,1,to[w]-1,-1);
            merg2(1,to[w],to[w],1000000000);
        }
        printf("Case #%d:",++N);
        for(int i=1;i<=n;i++) printf(" %d",ans[i]);
        printf("\n");
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值