当前位置: 首页 > news >正文

BZOJ3711 Druzyny 最值分治、线段树

传送门


被暴力包菜了,然而还不会卡……

有一个很暴力的DP:设\(f_i\)表示给\(1\)\(i\)分好组最多可以分多少组,转移枚举最后一个组。接下来考虑优化这个暴力。

考虑:对于每一个位置\(i\),设\(pre_i\)表示在仅考虑\(d\)的条件下右端点为\(i\)的所有满足条件的区间中最左的左端点的前一个位置。显然\(pre_i\)\(i\)的增大是不降的,而右端点为\(i\)的合法区间的左端点范围恰好为\([pre_i + 1 , i]\)

这里我们消除了\(d\)的条件的限制,那么只需要考虑\(c\),而一个区间的\(c\)的限制只取决于其最大值,所以考虑最大值分治。

设正在解决区间\([l,r]\),我们找到\([l+1,r]\)的最大值\(p\)(注意是\([l+1,r]\),因为\(f_l\)贡献到\(f_r\)时只考虑\(c_{[l+1,r]}\)的值),那么\(forall i \in [l,p) , j \in [p,r] , \max\limits_{k \in [i+1,j]} \{c_k\} = c_p\)。先递归\([l,p-1]\),然后将\([l,p-1]\)\([p,r]\)的贡献处理,然后解决\([p,r]\)

对于\([l,p-1]\)\([p,r]\)的贡献,注意到\(pre_i\)不降,所以对于每一个\(i \in [p,r]\),将转移分为三种情况:

\(pre_i \leq l , i - c_p < p - 1\),在这种情况下\(i\)位置会从一段前缀转移过来,而且\(i\)增加\(1\),前缀长度增加\(1\)。在第一次的时候用线段树查一下最前面一段的最大值,之后每一次\(O(1)\)地更新这个前缀的值。

\(pre_i \leq l , i - c_p \geq p - 1\),这种情况转移一定会是整个左区间。二分出满足\(pre_j \leq l\)的最大的\(j\)然后在线段树上区间修改。

\(pre_i > l\),这种情况暴力在线段树上查对应区间。

\(pre_i \geq p\),直接退出。

考虑复杂度:①中每一次只有一个\(log\)的查询和\(\min(p - l , r - p + 1)\)地查询,复杂度和最大值分治一致;②每一次只会有一个\(log\)的修改;③因为对于任意一个\(i\)\(pre_i\)在当前分治区域的左区间、\(i\)在当前分治区域的右区间的分治区域数量至多为\(1\),所以总复杂度是\(O(nlogn)\)。所以总共复杂度为\(O(nlogn)\)

一些细节:

1、卡空间……求\(c\)的最大值用线段树而不是ST表;

2、线段树区间打标记并不需要动态维护当前区间的最大\(c\)值和方案数,只需要在分治区域大小为\(1\)的时候在线段树上求一下这个位置的实际答案。

#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;
 
inline int read(){
    int a = 0;
    char c = getchar();
    while(!isdigit(c))
        c = getchar();
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return a;
}
 
const int MAXN = 1e6 + 1 , MOD =  1e9 + 7;
int add(int a , int b){return a + b >= MOD ? a + b - MOD : a + b;}
struct PII{
    int st , nd;
    PII(int _st = 0 , int _nd = 0) : st(_st) , nd(_nd){}
}dp[MAXN];
int C[MAXN] , D[MAXN] , pre[MAXN] , N;
//int ST[20][MAXN] , logg2[MAXN];
 
PII merge(PII a , PII b){
    if(a.st == b.st) return PII(a.st , add(a.nd , b.nd));
    return a.st > b.st ? a : b;
}
 
int cmp(int a , int b){return C[a] > C[b] ? a : b;}
namespace segTree2{
    int Max[MAXN << 2];
 
#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)
     
    void init(int x , int l , int r){
        if(l == r) Max[x] = l;
        else{
            init(lch , l , mid); init(rch , mid + 1 , r);
            Max[x] = cmp(Max[lch] , Max[rch]);
        }
    }
 
    int query(int x , int l , int r , int L , int R){
        if(l >= L && r <= R) return Max[x];
        int cur = 0;
        if(mid >= L) cur = query(lch , l , mid , L , R);
        if(mid < R) cur = cmp(cur , query(rch , mid + 1 , r , L , R));
        return cur;
    }
}
 
namespace segTree{
    struct node{
        PII maxN , mrk;
        node(){maxN = mrk = PII(-1e9 , 0);}
    }Tree[MAXN * 2 + 100000];
 
#define mid ((l + r) >> 1)
#define lch (x << 1)
#define rch (x << 1 | 1)
     
    void pushup(int x){Tree[x].maxN = merge(Tree[lch].maxN , Tree[rch].maxN);}
    void mark(int x , PII mrk){Tree[x].mrk = merge(Tree[x].mrk , mrk);}
    void pushdown(int x){
        mark(lch , Tree[x].mrk); mark(rch , Tree[x].mrk);
        Tree[x].mrk = PII(-1e9 , 0);
    }
 
    void modify(int x , int l , int r , int L , int R , PII mrk){
        if(l >= L && r <= R) return mark(x , mrk);
        pushdown(x);
        if(mid >= L) modify(lch , l , mid , L , R , mrk);
        if(mid < R) modify(rch , mid + 1 , r , L , R , mrk);
    }
 
    void update(int tar){
        int x = 1 , l = 0 , r = N;
        while(l != r){
            pushdown(x);
            if(mid >= tar){x = lch;  r = mid;}
            else{x = rch; l = mid + 1;}
        }
        dp[tar] = Tree[x].maxN = merge(dp[tar] , Tree[x].mrk);
        while(x >>= 1) pushup(x);
    }
     
    PII query(int x , int l , int r , int L , int R){
        if(L > R) return PII(-1e9 , 0);
        if(l >= L && r <= R) return Tree[x].maxN;
        pushdown(x);
        PII sum(-1e9 , 0);
        if(mid >= L) sum = merge(sum , query(lch , l , mid , L , R));
        if(mid < R) sum = merge(sum , query(rch , mid + 1 , r , L , R));
        return sum;
    }
}
 
priority_queue < int , vector < int > , greater < int > > q1 , q2;
void maintain(){while(!q2.empty() && q1.top() == q2.top()){q1.pop(); q2.pop();}}
void init(){
    /*for(int i = 2 ; i <= N ; ++i)
      logg2[i] = logg2[i >> 1] + 1;
      for(int i = 1 ; i <= N ; ++i)
      ST[0][i] = i;
      for(int i = 1 ; 1 << i <= N ; ++i)
      for(int j = 1 ; j + (1 << i) - 1 <= N ; ++j)
      ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);*/
    segTree2::init(1 , 1 , N);
    for(int i = 1 ; i <= N ; ++i){
        pre[i] = pre[i - 1];
        q1.push(D[i]); maintain();
        while(q1.top() < i - pre[i]){
            q2.push(D[++pre[i]]); maintain();
        }
    }
}
 
int qST(int x , int y){
    //int t = logg2[y - x + 1];
    //return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]);
    return segTree2::query(1 , 1 , N , x , y);
}
 
void solve(int l , int r){
    if(l == r)
        return segTree::update(l);
    int p = qST(l + 1 , r); solve(l , p - 1);
    int pos = max(p , l + C[p]);
    PII cur = segTree::query(1 , 0 , N , l , pos - C[p] - 1);
    while(pos <= r && pre[pos] <= l && pos - C[p] < p){
        cur = merge(cur , dp[pos - C[p]]);
        dp[pos] = merge(dp[pos] , PII(cur.st + 1 , cur.nd));
        ++pos;
    }
    if(pos <= r && pre[pos] <= l){
        int L = pos , R = r;
        while(L < R){
            int Mid = (L + R + 1) >> 1;
            pre[Mid] <= l ? L = Mid : R = Mid - 1;
        }
        segTree::modify(1 , 0 , N , pos , L , PII(cur.st + 1 , cur.nd));
        pos = L + 1;
    }
    while(pos <= r && pre[pos] < p){
        cur = segTree::query(1 , 0 , N , pre[pos] , min(pos - C[p] , p - 1));
        dp[pos] = merge(dp[pos] , PII(cur.st + 1 , cur.nd));
        ++pos;
    }
    solve(p , r);
}
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
#endif
    N = read();
    fill(dp + 1 , dp + N + 1 , PII(-1e9 , 0)); dp[0].nd = 1;
    for(int i = 1 ; i <= N ; ++i){C[i] = read(); D[i] = read();}
    init(); solve(0 , N);
    if(dp[N].st <= 0)
        puts("NIE");
    else
        printf("%d %d\n" , dp[N].st , dp[N].nd);
    return 0;
}

转载于:https://www.cnblogs.com/Itst/p/10735786.html

相关文章:

  • jmeter5.1企业级应用功能详解
  • 面向对象的三大特性之封装
  • 数据结构:自定义数组队列
  • 异或的性质及运用
  • 20175215 2018-2019-2 第八周java课程学习总结
  • 我的java问题排查工具单
  • 记录一个pom文件
  • 2.4 hive创建表实例讲解
  • Cookie Session和自定义分页
  • SSM框架的优势?
  • 获得小黄衫有感
  • Hello2 Analysis
  • exe4j 使用记录(二):jar打包exe
  • ModBus-RTU详解
  • 冲刺进度条-2
  • docker容器内的网络抓包
  • Java编程基础24——递归练习
  • Linux中的硬链接与软链接
  • mysql常用命令汇总
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • Python爬虫--- 1.3 BS4库的解析器
  • Vue.js源码(2):初探List Rendering
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Webpack 4 学习01(基础配置)
  • 聊聊directory traversal attack
  • 用简单代码看卷积组块发展
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (a /b)*c的值
  • (function(){})()的分步解析
  • (二)WCF的Binding模型
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (生成器)yield与(迭代器)generator
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (四)Linux Shell编程——输入输出重定向
  • (一) storm的集群安装与配置
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转载)Linux网络编程入门
  • .“空心村”成因分析及解决对策122344
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET开发不可不知、不可不用的辅助类(一)
  • :“Failed to access IIS metabase”解决方法
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解
  • [.net]官方水晶报表的使用以演示下载
  • [ASP.NET MVC]Ajax与CustomErrors的尴尬
  • [BUUCTF 2018]Online Tool
  • [C++] Windows中字符串函数的种类
  • [dts]Device Tree机制