[UOJ UNR#1]奇怪的线段树

来自FallDream的博客,未经允许,请勿转载, 谢谢。


 

原题可以到UOJ看,传送门

 

如果存在一个点是白的,却有儿子是黑的,显然无解。

不然的话,只要所有黑色的“黑叶子”节点,即没有黑色的儿子的节点有访问到就行了。

联想到今年CTSC上一道题叫“被操纵的线段树”,每个点被访问之后,可以和他合并的点满足左端点是它的右端点+1,并且和它没有相同的父亲。

发现这些点构成一条链,所以只需要向最大的那个点连边就行了,然后每个点向左儿子连边。

拆点之后,给所有的“黑色叶子”节点中间的边加上流量下界,求出最小流就是答案了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#define S 0
#define T 16001
#define SS 16002
#define TT 16003
#define MN 16003
#define INF 2000000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,q[MN+5],c[MN+5],d[MN+5],head[MN+5],cnt=1,Lt[MN+5],Rt[MN+5],dn=0,m;
int in[MN+5],L[MN+5],R[MN+5],s[MN+5],ss[MN+5],fa[MN+5],top;
struct edge{int to,next,w;}e[MN*100+5];
vector<int> v[MN+5];

inline void ins(int f,int t,int w)
{
    e[++cnt]=(edge){t,head[f],w};head[f]=cnt;
    e[++cnt]=(edge){f,head[t],0};head[t]=cnt;    
}

int Build(int l,int r)
{
    int x=++dn;s[x]=read();Lt[x]=l;Rt[x]=r;v[l].push_back(x);
    if(l!=r) 
    {
        int mid=read();
        fa[L[x]=Build(l,mid)]=x;
        fa[R[x]=Build(mid+1,r)]=x;
        ss[x]|=ss[L[x]]|ss[R[x]];
        if(!s[x]&&ss[x]){puts("OwO");exit(0);}
        ins(x,L[x],INF);
    }
    if(ss[x]|s[x]) ins(x,x+m,INF);
    if(s[x]&&!ss[x]) ins(S,x,INF),--in[x],++in[x+m],ins(x+m,T,INF);
    ss[x]|=s[x];return x;
}

bool bfs()
{
    memset(d,0,sizeof(d));int i,j;
    for(d[q[top=i=1]=SS]=1;i<=top;++i)
        for(j=c[q[i]]=head[q[i]];j;j=e[j].next)
            if(e[j].w&&!d[e[j].to]) d[q[++top]=e[j].to]=d[q[i]]+1;
    return d[TT];    
}

int dfs(int x,int f)
{
    if(x==TT) return f;int used=0;
    for(int&i=c[x];i;i=e[i].next)
        if(e[i].w&&d[e[i].to]==d[x]+1)
        {
            int w=dfs(e[i].to,min(f-used,e[i].w));
            used+=w;e[i].w-=w;e[i^1].w+=w;
            if(used==f) return f;
        }
    return d[x]=-1,used;
}

int main()
{
    n=read();m=2*n-1;Build(1,n);
    for(int i=1,j,k;i<=dn;++i)
        for(j=0,k=Rt[i]+1;j<v[k].size();++j)
            if(v[k][j]!=R[fa[i]]) {ins(i+m,v[k][j],INF);break;}
    for(int i=1;i<=dn<<1;++i) if(in[i]>0) ins(SS,i,in[i]); else if(in[i]<0) ins(i,TT,-in[i]);
    while(bfs()) dfs(SS,INF);ins(T,S,INF);
    while(bfs()) dfs(SS,INF);
    printf("%d\n",e[cnt].w);
    return 0;
}
posted @ 2017-09-01 13:13  FallDream  阅读(613)  评论(0编辑  收藏  举报