poj1201(二分+线段树)或(差分约束系统)

题意:数轴上每个位置为0或是1,给n(1 <= n <= 50000)个区间[ai, bi],每个区间内至少有 ci
个1.0 <= ai <= bi <= 50000,1 <= ci <= bi - ai+1。问数轴上至少有多少个1可以满足要求。

解法1:现将区间按右端点排序,然后每个区间内的点尽量往右边放,这样子可以照顾到以后的。在找每个区间的放法时,线段树查询区间1的个数,二分查找要放的后缀位置,然后将整个区间后缀全部涂上1.总复杂度是nlognlogn。网上没找到有人这么做的,但确实可以。

解法2: 将每个数轴的前缀的1的数量当做一个点。然后[ai,bi]之间有ci个点,就是点ai-1到点bi有个ci的边。然后每个位置最少0个1,最多1个1.所以ai 到 ai+1有个0长度的边,ai+1到ai有个-1长度的边。 
  然后就是求左端点到右端点的最长路了。

解法一代码:

/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=50010;
const LL INF=0x3FFFFFFF;
struct node
{
    int l,r;
    node* pl,*pr;
    bool same;
    int sum;
} nodes[Max*3];
int tot=0;
int n=0;
void build(node* p,int left,int right)
{
    p->l=left;
    p->r=right;
    p->sum=0;
    p->same=0;
    if(left==right)
        return ;

    int middle=(left+right)/2;

    tot++;
    p->pl=nodes+tot;
    build(p->pl,left,middle);

    tot++;
    p->pr=nodes+tot;
    build(p->pr,middle+1,right);
}
void push_down(node* p)
{
    int middle=(p->r+p->l)/2;
    p->pl->same=1;
    p->pl->sum=middle-p->l+1;

    p->pr->same=1;
    p->pr->sum=p->r-middle;

    p->same=0;
}
void pushup(node* p)
{
    p->sum=p->pr->sum+p->pl->sum;
}
void update(node* p,int left,int right)
{
    if(p->l==p->r)
    {
        p->same=1;
        p->sum=1;
        return ;
    }
    if(p->l==left&&p->r==right)
    {
        p->same=1;
        p->sum=right-left+1;
        return ;
    }
    if(p->same)
        push_down(p);

    int middle=(p->r+p->l)/2;

    if(left>middle)
        update(p->pr,left,right);
    else if(right<=middle)
        update(p->pl,left,right);
    else
    {
        update(p->pl,left,middle);
        update(p->pr,middle+1,right);
    }
    pushup(p);
}

int query(node* p,int left,int right)
{
    if(right<left)
    return 0;
    if(p->l==left&&p->r==right)
        return p->sum;
    if(p->same)
        push_down(p);

    int middle=(p->r+p->l)/2;
    if(left>middle)
        return query(p->pr,left,right);
    else if(right<=middle)
        return query(p->pl,left,right);
    else
        return query(p->pl,left,middle)+query(p->pr,middle+1,right);
}
struct line
{
    int x,y;
    int c;
} lines[Max];
bool operator<(const line& a,const line& b)
{
    if(a.y!=b.y)
        return a.y<b.y;
    return a.x<b.x;
}
void solve(int a,int b,int c)
{
    if(query(nodes,a,b)>=c)
    return ;
    int left=a,right=b;
    while(left<=right)
    {
        int middle=(left+right)/2;
        if(query(nodes,a,middle)+b-middle>=c)
        left=middle+1;
        else
        right=middle-1;
    }
    ///cout<<left<<endl;
    update(nodes,left,b);
}
int main()
{
    while(cin>>n)
    {
        tot=0;
        build(nodes,0,Max-1);
        for(int i=0; i<n; i++)
            scanf("%d%d%d",&lines[i].x,&lines[i].y,&lines[i].c);
        sort(lines,lines+n);
        for(int i=0; i<n; i++)
        {
            solve(lines[i].x,lines[i].y,lines[i].c);
            //cout<<lines[i].x<<"           "<<lines[i].y<<endl;
           // cout<<"   "<<query(nodes,1,Max-1)<<endl;
        }
        cout<<query(nodes,0,Max-1)<<endl;
    }
    return 0;
}

解法2代码:

/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=50010;
const LL INF=0x3FFFFFFF;
struct edge
{
    int u,v;
    int next;
    int len;
} edges[Max*3];
int tot=0;
int head[Max];
void add(int u,int v,int c)
{
    edges[tot].u=u;
    edges[tot].v=v;
    edges[tot].len=c;
    edges[tot].next=head[u];
    head[u]=tot++;
}
int dist[Max];
int n;
int  mi=Max;
int ma=0;
int que[Max*10];
bool rem[Max];
void spfa()
{
    memset(rem,0,sizeof rem);
    dist[mi]=0;
    rem[mi]=1;
    int left=0;
    int right=1;
    que[0]=mi;
    while(left<right)
    {
        int t=que[left++];
        rem[t]=0;
        for(int i=head[t]; i!=-1; i=edges[i].next)
        {
            int ne=edges[i].v;
            if(dist[ne]<dist[t]+edges[i].len)
            {
                dist[ne]=dist[t]+edges[i].len;
                if(!rem[ne])
                {
                    que[right++]=ne;
                    rem[ne]=1;
                }
            }
        }
    }
}
int main()
{
    cin>>n;
    memset(head,-1,sizeof head);
    for(int i=0; i<n; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        a++;b++;
        mi=min(mi,a-1);
        ma=max(ma,b);
        add(a-1,b,c);
    }
    for(int i=mi; i<=ma; i++)
    {
        add(i,i+1,0);
        add(i+1,i,-1);
        dist[i]=-INF;
    }
    spfa();
    cout<<dist[ma]<<endl;
    return 0;
}
时间: 2024-10-04 19:46:59

poj1201(二分+线段树)或(差分约束系统)的相关文章

【BZOJ-3110】K大数查询 整体二分 + 线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5008 Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is

(困难) CF 484E Sign on Fence,整体二分+线段树

Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks follow without a gap between them. Afte

zoj 3888 Twelves Monkeys 二分+线段树维护次小值

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3888 Twelves Monkeys Time Limit: 5 Seconds      Memory Limit: 32768 KB James Cole is a convicted criminal living beneath a post-apocalyptic Philadelphia. Many years ago, the Earth's surf

bzoj1568: [JSOI2008]Blue Mary开公司 三分+二分+线段树

答案序列一定是个下凸壳,因此添加的等差数列与其之差是个单峰函数,可以先三分求出最值,再二分求出零点,然后用线段树,将得到的区间修改为一个等差数列. 这个做法应该比较好想吧,虽然比较慢…… #include<cstdio> #define Z int l=1,int r=N,int k=1 #define N 50000 #define M (l+r>>1) #define P (k<<1) #define S (k<<1|1) #define K l,r,k

计蒜客16492 building(二分线段树/分块)

题解: 考虑用线段树维护楼的最大值,然后这个问题就很简单了. 每次可以向左二分出比x高的第一个楼a,同理也可以向右二分出另一个楼b,如果a,b都存在,答案就是b-a-1. 注意到二分是可以直接在线段树上进行的,所以复杂度是O(nlogn). 当然这里是用分块做的,更暴力一些. #include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace st

51nod1287(二分/线段树区间最值&amp;单点更新)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1287 题意:中文题诶- 解法1:b[i] 存储 max(a[0], ....., a[i]),显然 b 是单调不减的,所以直接二分 x,再更新 a 和 b 数组即可: 代码: 1 #include <iostream> 2 #include <stdio.h> 3 using namespace std; 4 5 const int MAXN =

BZOJ 2527 Poi2011 Meteors 整体二分+线段树 / 可持久化线段树(MLE)

题目大意:给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值 首先我们考虑暴力想法 对于每个国家分开讨论 二分操作次数 但是这样每次Judge的时候我们要模拟1~mid所有的操作 浪费在这里的复杂度实在太大 这样做每个国家需要模拟O(klogk)次操作 时间复杂度O(nklogk) TLE 我们需要对浪费在这里的复杂度做一些改进 1.可持久化线段树(MLE) 每次二分一个mid之后 我们要找到mid次

hdu6070(分数规划/二分+线段树区间更新,区间最值)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意: 给出一个题目提交序列, 从中选出一个正确率最小的子串. 选中的子串中每个题目当且仅当最后一次提交是正确的. 思路: 分数规划 二分答案, 然后在 check 函数中查找是否存在某个区j间 [l, r] 使得 sum(l, r) / (r - l + 1) <= mid, 即 sum(l, r) + l * mid <= (r + 1) * mid. 可以用个线段树来维护 sum(l