UOJ 217 奇怪的线段树

http://uoj.ac/problem/217

题意就不X了,思路在这:

居然一开始把sap里面的mn设置为inf了,我是傻逼。。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f
int go[200005],first[200005],next[200005],flow[200005],op[200005],dis[200005],cnt[200005];
int tot,S,T,t,s,sz,son[200005],du[200005],n,nodes;
bool flag;
int read(){
    int t=0,f=1;char ch=getchar();
    while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘) f=-1;ch=getchar();}
    while (‘0‘<=ch&&ch<=‘9‘){t=t*10+ch-‘0‘;ch=getchar();}
    return t*f;
}
void insert(int x,int y,int z){
    tot++;
    go[tot]=y;
    next[tot]=first[x];
    first[x]=tot;
    flow[tot]=z;
}
void add(int x,int y,int z){
    insert(x,y,z);op[tot]=tot+1;
    insert(y,x,0);op[tot]=tot-1;
}
void add(int x,int y,int low,int high){
    int F=high-low;
    add(x,y,F);du[x]-=low;du[y]+=low;
}
int build(int l,int r,int op){
    int k=++sz;
    add(s,k,0,inf);add(k+2*n-1,t,0,inf);
    add(4*n-2+l,k,0,inf);if (op&&r<n) add(k+2*n-1,4*n-2+r+1,0,inf);
    if (!op) add(5*n-2+l,k,0,inf);
    if (!op&&r<n) add(k+2*n-1,5*n-2+r+1,0,inf);
    int x=read();
    if (l==r){
       son[k]=x;
       return x;
    }
    int mid=read();
    int p=build(l,mid,0),q=build(mid+1,r,1);
    if (p||q){
        if (!x) flag=0;
        son[k]=2;
        return 1;
    }
    return son[k]=x;
}
int dfs(int x,int f){
    if (x==T) return f;
    int mn=nodes,sum=0;
    for (int i=first[x];i;i=next[i]){
        int pur=go[i];
        if (dis[pur]+1==dis[x]&&flow[i]){
            int F=std::min(flow[i],f-sum);
            int save=dfs(pur,F);
            sum+=save;
            flow[i]-=save;
            flow[op[i]]+=save;
            if (sum==f||dis[S]>=nodes) return sum;
        }
        if (flow[i]) mn=std::min(mn,dis[pur]);
    }
    if (sum==0){
        cnt[dis[x]]--;
        if (cnt[dis[x]]==0){
           dis[S]=nodes;
        }else{
           dis[x]=mn+1;
           cnt[dis[x]]++;
        }
    }
    return sum;
}
int mxflow(){
    int res=0,tim=0;
    while (dis[S]<nodes){
        tim++;
        res+=dfs(S,inf);
    }
    return res;
}
int main(){
    n=read();
    s=0;t=6*n-1;
    flag=1;
    build(1,n,0);
    if (!flag) {puts("OwO");return 0;}
    for (int i=1;i<=sz;i++)
        if (son[i]==1)
            add(i,i+sz,1,inf);
        else
        if (son[i]==2)
            add(i,i+sz,0,inf);
    S=t+1,T=S+1;nodes=T+1;
    for (int i=s;i<=t;i++)
        if (du[i]>0) add(S,i,du[i]);
        else if (du[i]<0) add(i,T,-du[i]);
    int ans1=mxflow();
    memset(dis,0,sizeof dis);
    memset(cnt,0,sizeof cnt);
    add(t,s,inf);
    int ans=mxflow();
    printf("%d\n",ans);
}
时间: 2024-10-10 22:43:45

UOJ 217 奇怪的线段树的相关文章

【UNR #1】奇怪的线段树

SOL: 好像可以贪心啊,一颗树的左儿子的右儿子和右儿子的左儿子合并.其他递归处理. #pragma GCC optimize("-Ofast") #include<bits/stdc++.h> using namespace std; int n,lx,rx,mx,ux,ans; int sol(int op,int l,int r,int &lx,int &rx,int &mx,int &ux) { int x; scanf("

【vijos】1750 建房子(线段树套线段树+前缀和)

https://vijos.org/p/1750 是不是我想复杂了.... 自己yy了个二维线段树,然后愉快的敲打. 但是wa了两法.......sad 原因是在处理第二维的更新出现了个小问题,sad. void pushup1(int x) { for1(i, 1, mm<<2) mn[x][i]=min(mn[lc][i], mn[rc][i]); } 这里注意是mm*4...我该好好想想了..这是在dbg的时候找出来的问题.sad. 我觉得很奇怪,线段树的底层节点一共就mm个,那么整棵树

POJ2374 Fence Obstacle Course 【线段树】

题目链接 POJ2374 题解 题意: 给出\(n\)个平行于\(x\)轴的栅栏,求从一侧栅栏的某个位置出发,绕过所有栅栏到达另一侧\(x = 0\)位置的最短水平距离 往上说都是线段树优化dp 我写了一个奇怪的线段树过了,似乎并没有和dp沾边 因为每次都是从某个栅栏的端点出发,到达某个位置的值等于[所有这些可出发的端点已产生的代价 + 到达这个点的距离] 的最小值 就用线段树维护每个区间 \(lm[u]\)表示在这个区间内的端点到达左端点的最小代价 \(rm[u]\)表示到右端点的最小代价 然

UOJ #164 【清华集训2015】V (线段树)

题目链接 http://uoj.ac/problem/164 题解 神仙线段树题. 首先赋值操作可以等价于减掉正无穷再加上\(x\). 假设某个位置从前到后的操作序列是: \(x_1,x_2,...,x_k\) 那么则有: 当前值就是该序列的最大后缀和,历史最大值就是该序列的最大子段和! 然后如果把最大子段和定义加法,那么就变成了区间加单点查询. 直接线段树维护即可,时间复杂度\(O(n\log n)\). (好吧,其实似乎把赋值看做减去正无穷再加\(x\)似乎是可以被卡爆long long的-

BZOJ 3878 Ahoi2014 奇怪的计算器 线段树

题目大意:给定n个操作,每个操作有四种形式,操作之后若<L就变成L,>R就变成R,现在给定q个输入,求他们的输出 n,q<=10W 将这q个数建立线段树,四个操作都可以在线段树上完成 但是溢出怎么办呢? 容易发现若x<=y,那么操作过后x一定<=y 因为四个操作都是线性的,溢出也不会反转两个数的大小关系 那么我们可以预先将q个数排序 那么溢出的数一定是连续的两段 区间修改就行了 至于怎么找两端溢出的区间-- 每个节点维护一个区间最大值和最小值就好了 此外数据还是比较良心的 不

[UOJ #222][NOI2016]区间(线段树)

Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值 求所有合法方案中最小的花费.如果不存在合法的方案,输出 −1. S

【BZOJ3878】【Ahoi2014】奇怪的计算器 维护区间性质。线段树

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44037685"); } 题解: 先排序然后插入线段树 用线段树每次对全区间进行操作. 然后维护哪些段区间溢出了,对这段区间进行赋值. 溢出处理: 一个区间的左端点大于最大值,或者右端点小于最小值 那么这个区间就该被覆盖. 覆盖,加特

uoj#388. 【UNR #3】配对树(线段树合并)

传送门 先考虑一个贪心,对于一条边来说,如果当前这个序列中在它的子树中的元素个数为奇数个,那么这条边就会被一组匹配经过,否则就不会 考虑反证法,如果在这条边两边的元素个数都是偶数,那么至少有两组匹配经过它,那么把这两条路径都删去这条边可以更优.如果两边是奇数,一定至少有一条路径经过它,去掉这组匹配之后就变成了偶数的情况.证毕 然后是一个神仙的转化,我们对于一颗子树中的元素,在序列里标记为\(1\),否则为\(0\),那么这条边出现次数就是序列中长度为偶数且区间和为奇数的区间个数 考虑用线段树合并

UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$,点 x 在原树上的深度为 $d[x]$,那么 $$d[x]+d[y] - d[LCA(x,y)] - d'[LCA(x,y)] = \frac 12(D[x] + d[x]) + \frac 12 (D[y] + d[y]) - d'[LCA(x,y)]$$ 于是我们考虑将分治区域内的节点在第二棵