1974: #6032. 「雅礼集训 2017 Day2」水箱

文章描述了一个关于计算在给定高度条件下的多挡板水箱中,最多可以同时满足多少个条件的问题,涉及区间更新和查询的数据结构优化算法。
摘要由CSDN通过智能技术生成

题目描述

给出一个长度为 n nn 宽度为 1 11,高度无限的水箱,有 n−1 n - 1n−1 个挡板将其分为 n nn 个 1 - 1 的小格,然后向每个小格中注水,水如果超过挡板就会溢出到挡板的另一边,这里的水是满足物理定律的(在无挡板阻拦的情况下会向低处流),现在有 m mm 个条件 (i,y,k) (i, y, k)(i,y,k),表示从左到右数的第 i ii 个格子中,在高度为 y+0.5 y + 0.5y+0.5 的地方是否有水,k=1 k = 1k=1 表示有水,k=0 k = 0k=0 表示没有水,请求出这 m mm 个条件最多能同时满足多少个条件。本题有多组数据。

输入

第一行一个正整数 T TT,为数据组数。
第二行两个正整数 n nn、m mm,中间用空格隔开。
接下来一行 n−1 n - 1n−1 个整数,表示从左到右每一块隔板的高度。
接下来 m mm 行,每行三个整数 i ii、y yy、k kk,表示一个条件。

输出

共 T TT 行,每行对应一组数据的答案。

样例输入 

2
3 4
3 4
1 3 1
2 1 0
2 2 0
3 3 1
2 2
2
1 2 0
1 2 1

样例输出 

3
1

提示

对于 20% 20\%20% 的数据,n,m≤16 n, m \leq 16n,m≤16;
对于另外 10% 10\%10% 的数据,只存在指明某处有水的条件;
对于另外 30% 30\%30% 的数据,n,m≤1000 n, m \leq 1000n,m≤1000;
对于 100% 100\%100% 的数据,n,m≤105,T≤5 n, m \leq 10 ^ 5, T \leq 5n,m≤105,T≤5。

#include<map>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define ULL unsigned long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) ((-x) & (x))
#pragma GCC optimize(2)
LL read() {
    LL f = 1,x = 0;char s = getchar();
    while(s < '0' || s > '9') {if(s == '-')f=-f;s = getchar();}
    while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
    return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
int a[MAXN],b[MAXN];
int sm[MAXN],w[MAXN];
int dp[MAXN];
vector<int> col[MAXN];
struct qu{
    int i,j,l,r;
}ex[MAXN],no[MAXN];
inline bool cm1(qu a,qu b) {return a.j > b.j;}
int cte,ctn;
struct it{
    int id,h;
}hi[MAXN];
inline bool cmp(it a,it b) {return a.h > b.h;}
//------------------------------------------ZKW
int rr[MAXN<<2],ll[MAXN<<2],M;
void maketree(int n) {
    M=1;while(M < n+2)M <<= 1;
    for(int i=(M<<1|1);i>0;i--)rr[i]=n;
    memset(ll,0,sizeof(ll));
}
void addr(int l,int r,int ad) {
    int s = M + l - 1,t = M + r + 1;
    while(s || t) {
        if((s>>1) != (t>>1)) {
            if(!(s & 1)) rr[s^1] = min(rr[s^1],ad);
            if(t & 1) rr[t^1] = min(rr[t^1],ad);
        }else break;
        s >>= 1;t >>= 1;
    }return ;
}
void addl(int l,int r,int ad) {
    int s = M + l - 1,t = M + r + 1;
    while(s || t) {
        if((s>>1) != (t>>1)) {
            if(!(s & 1)) ll[s^1] = max(ll[s^1],ad);
            if(t & 1) ll[t^1] = max(ll[t^1],ad);
        }else break;
        s >>= 1;t >>= 1;
    }return ;
}
void query(int x,int &l,int &r) {
    int s = M + x;l = 0;r = 0x7f7f7f7f;
    while(s) {l = max(l,ll[s]);r = min(r,rr[s]);s>>=1;}
    return ;
}
//-----------------------------------ZKW_End
int c[MAXN];
void addtree(int x,int y) {while(x<=n)c[x] += y,x += lowbit(x);}
inline int sum(int x) {int as=0;while(x>0)as += c[x],x -= lowbit(x);return as;}
int main() {
//  freopen("tank.in","r",stdin);
//  freopen("tank.out","w",stdout);
    int T = read();
    while(T --) {
        n = read();m = read();
        b[0] = b[n] = 0x7f7f7f7f;
        for(int i = 1;i < n;i ++) {
            b[i] = read();
            hi[i].id = i;hi[i].h = b[i];
        }
        for(int i = 1;i <= n;i ++) {
            sm[i] = 0; col[i].clear();
        }
        bool flag = 1;
        maketree(n);
        cte = ctn = 0;
        for(int i = 1;i <= m;i ++) {
            s = read();o = read();k = read();
            if(k == 1) {
                ex[++ cte].i = s;
                ex[cte].j = o;
            }
            else no[++ ctn].i = s,no[ctn].j = o,sm[s] ++;
        }
        sort(ex + 1,ex + 1 + cte,cm1);
        sort(no + 1,no + 1 + ctn,cm1);
        sort(hi + 1,hi + n,cmp);
        int j = 1;
        for(int i = 1;i < n;i ++) {
            while(j <= cte && ex[j].j >= hi[i].h) {
                query(ex[j].i,ex[j].l,ex[j].r);
                ex[j].l ++;j ++;
            }
            addr(1,hi[i].id,hi[i].id);
            addl(hi[i].id+1,n,hi[i].id);
        }
        while(j <= cte) {query(ex[j].i,ex[j].l,ex[j].r);ex[j].l ++;j ++;}
        j = 1;
        memset(c,0,sizeof(c));
        for(int i = 1;i <= cte;i ++) {
            while(j <= ctn && no[j].j > ex[i].j)
                addtree(no[j].i,1),j ++;
            w[i] = sum(ex[i].r) - sum(ex[i].l-1);
        }
        memset(c,0,sizeof(c));
        for(int i = cte;i > 0;i --) {
            addtree(ex[i].i,1);
            col[ex[i].r].push_back(i);
            w[i] += sum(ex[i].r) - sum(ex[i].l-1);
        }
        memset(dp,0,sizeof(dp));
        int ans = 0;
        for(int i = 1;i <= n;i ++) {
            dp[i] = dp[i-1] + sm[i];
            for(int j = 0;j < (int)col[i].size();j ++) {
                int l = ex[col[i][j]].l;
                int wi = w[col[i][j]];
                dp[i] = max(dp[i],dp[l-1] + wi);
            }
            ans = max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值