BZOJ 3681 线段树合并+网络流

思路:

暴力建图有n*m条边

考虑怎么优化

(那就只能加个线段树了呗)

然后我就不会写了.....

抄了一波题解

//By SiriusRen
#include <bits/stdc++.h>
using namespace std;
const int N=10050,M=N*100,inf=0x3f3f3f3f;
vector<int>vec[N];
int n,m,first[M],next[M],v[M],w[M],tot,cnt=2,S=1,T=2;
int lson[M],rson[M],root[N],jy,vis[M];
int read(){
    char p=getchar();int x=0;
    while(p<‘0‘||p>‘9‘)p=getchar();
    while(p>=‘0‘&&p<=‘9‘)x=x*10+p-‘0‘,p=getchar();
    return x;
}
void Add(int x,int y,int z){v[tot]=y,w[tot]=z,next[tot]=first[x],first[x]=tot++;}
void add(int x,int y,int z){Add(x,y,z),Add(y,x,0);}
int insert(int l,int r,int pos,int num){
    pos=++cnt;
    if(l==r){add(cnt,T,1);return pos;}
    int mid=(l+r)>>1;
    if(mid<num)add(pos,rson[pos]=insert(mid+1,r,rson[pos],num),inf);
    else add(pos,lson[pos]=insert(l,mid,lson[pos],num),inf);
    return pos;
}
int merge(int l,int r,int pos,int last){
    if(pos*last==0)return pos+last;
    int now=++cnt,mid=(l+r)>>1;
    if(l==r){add(now,pos,inf),add(now,last,inf);return now;}
    add(now,lson[now]=merge(l,mid,lson[pos],lson[last]),inf);
    add(now,rson[now]=merge(mid+1,r,rson[pos],rson[last]),inf);
    return now;
}
void dfs(int x){
    for(int i=0;i<vec[x].size();i++)
        dfs(vec[x][i]),root[x]=merge(1,n,root[x],root[vec[x][i]]);
}
void query(int l,int r,int pos,int L,int R){
    if(!pos)return;
    if(l>=L&&r<=R){add(cnt,pos,inf);return;}
    int mid=(l+r)>>1;
    if(mid<L)query(mid+1,r,rson[pos],L,R);
    else if(mid>=R)query(l,mid,lson[pos],L,R);
    else query(l,mid,lson[pos],L,R),query(mid+1,r,rson[pos],L,R);
}
bool tell(){
    memset(vis,-1,sizeof(vis));
    queue<int>q;q.push(S),vis[S]=0;
    while(!q.empty()){
        int t=q.front();q.pop();
        for(int i=first[t];~i;i=next[i])
            if(vis[v[i]]==-1&&w[i])vis[v[i]]=vis[t]+1,q.push(v[i]);
    }return ~vis[T];
}
int zeng(int x,int y){
    if(x==T)return y;
    int r=0;
    for(int i=first[x];~i;i=next[i])if(vis[v[i]]==vis[x]+1&&w[i]){
        int t=zeng(v[i],min(w[i],y-r));
        w[i]-=t,w[i^1]+=t,r+=t;
    }if(!r)vis[x]=-1;
    return r;
}
int flow(){int ans=0;while(tell())while(jy=zeng(S,inf))ans+=jy;return ans;}
int main(){
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++)vec[read()].push_back(i);
    for(int i=1;i<=n;i++)root[i]=insert(1,n,root[i],read());
    dfs(1);
    for(int i=1;i<=m;i++){
        int l=read(),r=read(),x=read(),y=read();
        cnt++,add(S,cnt,y),query(1,n,root[x],l,r);
    }
    printf("%d\n",flow());
}
时间: 2024-12-26 06:38:14

BZOJ 3681 线段树合并+网络流的相关文章

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

[BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树与右子树的逆序对” 是否会减小. 因此我们要求出两种情况下的逆序对数,使用线段树合并,对每个节点建一棵线段树,然后合并的同时就求出两种情况下的逆序对. 代码 #include <iostream> #include <cstdli

bzoj 4631: 踩气球 线段树合并

4631: 踩气球 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 265  Solved: 136[Submit][Status][Discuss] Description 六一儿童节到了, SHUXK 被迫陪着M个熊孩子玩一个无聊的游戏:有N个盒子从左到右排成一排,第i个盒子里装着Ai个气球. SHUXK 要进行Q次操作,每次从某一个盒子里拿出一个没被踩爆的气球,然后熊孩子们就会立刻把它踩爆. 这M个熊孩子每个人都指定了一个盒子区间[Li, R

bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212 思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换. 实现代码: #include<bits/stdc++.h> using namespace std; #define ll long long const int M = 4e5+10; int n,cnt,idx; ll ans,cnt1,cnt2; int v[M],l[M],r[M],root[

bzoj 3413: 匹配 后缀自动机+线段树合并

并不是很难啊,把细节想好了再写就很轻松了~ code: #include <bits/stdc++.h> #define N 200003 #define LL long long #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; struct SAM { int tot,last

神奇的操作——线段树合并(例题: BZOJ2212)

什么是线段树合并? 首先你需要动态开点的线段树.(对每个节点维护左儿子.右儿子.存储的数据,然后要修改某儿子所在的区间中的数据的时候再创建该节点.) 考虑这样一个问题: 你现在有两棵权值线段树(大概是用来维护一个有很多数的可重集合那种线段树,若某节点对应区间是\([l, r]\),则它存储的数据是集合中\(\ge l\).\(\le r\)的数的个数),现在你想把它们俩合并,得到一棵新的线段树.你要怎么做呢? 提供这样一种算法(tree(x, y, z)表示一个左儿子是x.右儿子是y.数据是z的

启发式合并(堆、set、splay、treap)/线段树合并学习小记

启发式合并 刚听到这个东西的时候,我是相当蒙圈的.特别是"启发式"这三个字莫名的装逼,因此之前一直没有学. 实际上,这个东西就是一个SB贪心. 以堆为例,若我们要合并两个堆a.b,我们有一种极其简单的做法:那就是比较一下它们的大小,将小的堆的每个元素依次插入到大的堆中.不妨设\(|a|≤|b|\),则时间复杂度即为:\(O(|a|*log_2(|a|+|b|))\). 这个东西看似很慢,但当点数较小的时候,我们可以证明复杂度是可被接受的. 比如我们要合并n个堆,这n个堆共有m个点.设这

【BZOJ4399】魔法少女LJJ 线段树合并

[BZOJ4399]魔法少女LJJ Description 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味:小猴在枝头悠来荡去,好不自在:各式各样的鲜花争相开放,各种树枝的枝头挂满沉甸甸的野果:鸟儿的歌声婉转动听,小河里飘着落下的花瓣真是人间仙境”SHY觉得LJJ还是太naive,一天,SHY带着自己心爱的图找到LJJ,对LJJ说:“既然你已经见识过动态树

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.