P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并
LINK:Druzyny
这题研究了一下午 终于搞懂了.
\(n^2\)的dp很容易得到.
考虑优化.又有大于的限制又有小于的限制这个非常难处理.
不过可以得到在限制人数上界的情况下能转移到的最远端点 且这个数组是单调的.
而下界是随意的.
这个可以利用单调队列做 也可以暴力线段树.
然后考虑怎么优化 考虑CDQ分治 不过这里算是基于时间的分治吧.
大体上就是求出来左边让左边对右边进行贡献.
考虑这部分怎么做 左边为 \([l,mid]\)右边为\(mid+1,r\) 分别设两个决策\(j,i\)分属这两个集合.
容易得到\(left_i\leq j,C_{j+1,i}\leq i-j\)后者的限制条件还是需要暴力转移.
考虑对最大值进行分治 即对笛卡尔树上每个节点进行分治 这样C的条件就很明朗了.
设分治中心为x 那么左区间为\(l,x-1\)右区间为\(x,r\)
刚才的条件就变成了\(left_i\leq j,C_x\leq i-j\)
考虑右边区间小的话就暴力扫右边区间 算出对应左区间 然后线段树上查区间最大值.
左边小的话 暴力搜啊左边二分出\(left\)的合法端点 算出对应右区间 然后线段树上区间修改.
可以发现上述暴力扫的过程类似启发式合并 这样复杂度为\(nlog^2n\)还不足以通过此题.
正解是分类讨论了几种情况:
首先我们知道 对于\(left_i\)是单调递增的 根据这个\(left_i\)可以划分三个区间.
分别为\(left_i<l,l<=left_i<x,x=<left_i\)
第三个可以忽略 第二个考虑这是一个横跨分治中心的 且在分治区间中的.
每个i最多在一个分治区间中会发生这种情况.这个部分可以线段树暴力查区间最大值.所以复杂度为\(nlogn\)
考虑第三种情况 这种情况下.
继续分类讨论考虑左边大还是右边大.
右边大的话 左边暴力扫 分两种情况 一种是 \(i<x+C_x\)这种情况可以在暴力扫的时候更新.一种是\(i>=x+C_x\)这种可以等右边扫完线段树区间修改.
前者启发式合并\(nlogn\) 后者每个节点处至多进行一次复杂度\(nlogn\)
左边大的话 右边暴力扫 先对右边最近节点扫到的j即\(i-C_x\)这个位置之前的位置线段树求max.然后再扫不断进行更新.
前者每个节点至多进行一次 复杂度\(nlogn\) 后者还是启发式合并\(nlogn\)
还有就是当前区间分成三段的节点可以二分.
这样总复杂度为\(nlogn\) 可以通过本题.
非常妙的分类讨论.
需要注意的是 这题卡空间 不动态开点 利用id函数即可使得儿子之间有序标号.空间为(2n).
code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-10
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
#define id(i,j) ((i+j)|(i!=j))
#define max(x,y) ((x)<(y)?y:x)
using namespace std;
char *fs,*ft,buf[1<<15];
inline char gc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
RE int x=0,f=1;RE char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;
}
const int MAXN=1000010;
int n,top,rt;
int c[MAXN],d[MAXN];
int le[MAXN],s[MAXN];
int ls[MAXN],rs[MAXN];
struct wy
{
int v,cnt;
wy(){v=-INF;cnt=0;}
wy(int _v,int _cnt){v=_v;cnt=_cnt;}
inline friend wy operator +(wy a,wy b)
{
if(a.v==-INF)return b;
if(b.v==-INF)return a;
if(a.v>b.v)return a;
if(b.v>a.v)return b;
return wy(a.v,a.cnt+b.cnt>=mod?a.cnt+b.cnt-mod:a.cnt+b.cnt);
}
}cc;
inline wy calc(wy b)
{
return b.v==-INF?b:wy(b.v+1,b.cnt);
}
wy tag[MAXN<<1],mx[MAXN<<1],f[MAXN];
inline void build()
{
top=0;
rep(1,n,i)
{
int now=0;
while(top&&c[s[top]]<=c[i])rs[s[top]]=now,now=s[top--];
s[++top]=i;ls[i]=now;
}
rt=s[1];
while(top>1)rs[s[top-1]]=s[top],--top;
}
inline void pushdown(int l,int r)
{
int mid=(l+r)>>1;
int ww=id(l,r),w1=id(l,mid),w2=id(mid+1,r);
tag[w1]=tag[w1]+tag[ww];
mx[w1]=mx[w1]+tag[ww];
tag[w2]=tag[w2]+tag[ww];
mx[w2]=mx[w2]+tag[ww];
tag[ww]=f[n+1];
}
inline wy ask(int l,int r,int L,int R)
{
if(L>R)return f[n+1];
if(L<=l&&R>=r)return mx[id(l,r)];
int mid=(l+r)>>1;
if(tag[id(l,r)].v>=0)pushdown(l,r);
if(R<=mid)return ask(l,mid,L,R);
if(L>mid)return ask(mid+1,r,L,R);
return ask(l,mid,L,R)+ask(mid+1,r,L,R);
}
inline void modify(int l,int r,int L,int R)
{
if(L>R)return;
if(L<=l&&R>=r)
{
int ww=id(l,r);
mx[ww]=mx[ww]+cc;
tag[ww]=tag[ww]+cc;
return;
}
int mid=(l+r)>>1;
if(tag[id(l,r)].v>=0)pushdown(l,r);
if(R<=mid)modify(l,mid,L,R);
else
{
if(L>mid)modify(mid+1,r,L,R);
else modify(l,mid,L,R),modify(mid+1,r,L,R);
}
mx[id(l,r)]=mx[id(l,mid)]+mx[id(mid+1,r)];
}
inline void solve(int l,int r,int x)
{
if(l==r)
{
cc=f[l];modify(0,n,l,r);
f[l]=ask(0,n,l,r);
return;
}
solve(l,x-1,ls[x]);
int p=lower_bound(le+x,le+r+1,l)-le;
int q=lower_bound(le+x,le+r+1,x)-le;
if(x-l<r-x+1)
{
wy ww;
vep(l,x,i)
{
ww=ww+f[i];
if(i+c[x]>=x&&i+c[x]<p)
f[i+c[x]]=f[i+c[x]]+calc(ww);
}
cc=calc(ww);modify(0,n,x+c[x],p-1);
}
else
{
wy ww=ask(0,n,l,x-c[x]-1);
vep(x,p,i)
{
if(i-c[x]>=l&&i-c[x]<x)ww=ww+f[i-c[x]];
f[i]=f[i]+calc(ww);
}
}
vep(p,q,i)f[i]=f[i]+calc(ask(0,n,le[i],min(x-1,i-c[x])));
solve(x,r,rs[x]);
}
int main()
{
//freopen("1.in","r",stdin);
get(n);
rep(1,n,i)get(c[i]),get(d[i]);
int h=1,t=0;
rep(1,n,i)
{
le[i]=le[i-1];
while(h<=t&&d[i]<=d[s[t]])--t;
s[++t]=i;
while(i-le[i]>d[s[h]])
{
++le[i];
if(le[i]>=s[h])++h;
}
//cout<<le[i]<<endl;
}
build();f[0]=wy(0,1);
solve(0,n,rt);
if(f[n].v==-INF)puts("NIE");
else printf("%d %d\n",f[n].v,f[n].cnt);
return 0;
}
P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并的更多相关文章
- BZOJ-1067 降雨量 线段树+分类讨论
这道B题,刚的不行,各种碎点及其容易忽略,受不鸟了直接 1067: [SCOI2007]降雨量 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 2859 ...
- bzoj 3779 重组病毒 好题 LCT+dfn序+线段树分类讨论
题目大意 1.将x到当前根路径上的所有点染成一种新的颜色: 2.将x到当前根路径上的所有点染成一种新的颜色,并且把这个点设为新的根: 3.查询以x为根的子树中所有点权值的平均值. 分析 原题codec ...
- SPOJ 2916 Can you answer these queries V(线段树-分类讨论)
题目链接:http://www.spoj.com/problems/GSS5/ 题意:给出一个数列.每次查询最大子段和Sum[i,j],其中i和j满足x1<=i<=y1,x2<=j& ...
- 美团CodeM初赛B轮 合并字符串的价值 (线段树,分类讨论)
输入两个字符串a和b,合并成一个串c,属于a或b的字符在c中顺序保持不变.如"ACG"和"UT"可以被组合成"AUCTG"或"AC ...
- 2020牛客暑假多校训练营 第二场 H Happy Triangle set 线段树 分类讨论
LINK:Happy Triangle 这道题很容易. 容易想到 a+b<x a<x<b x<a<b 其中等于的情况在第一个和第三个之中判一下即可. 前面两个容易想到se ...
- UVALive 7148 LRIP【树分治+线段树】
题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...
- 【loj6145】「2017 山东三轮集训 Day7」Easy 动态点分治+线段树
题目描述 给你一棵 $n$ 个点的树,边有边权.$m$ 次询问,每次给出 $l$ .$r$ .$x$ ,求 $\text{Min}_{i=l}^r\text{dis}(i,x)$ . $n,m\le ...
- 【BZOJ4372】烁烁的游戏 动态树分治+线段树
[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠.题意:给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠.烁烁他每次会跳到一个节点u,把周围与他距 ...
- 【bzoj4372】烁烁的游戏 动态点分治+线段树
题目描述 给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:Q x:询问x的点权.M x d w:将树上与节点x距离不超过d的节点的点权均加上w. 输入 第一行两个正整数:n,m接下来的n-1 ...
随机推荐
- Github Actions简单部署一个vue/react项目
大体介绍 本文对github actions部署前端项目做一个简单的总结,总体来说,我感觉用它想要部署一个前端项目,可以说非常简单,简单得令人震惊
- java IO流 (七) 对象流的使用
1.对象流: ObjectInputStream 和 ObjectOutputStream2.作用:ObjectOutputStream:内存中的对象--->存储中的文件.通过网络传输出去:序列 ...
- 05 drf源码剖析之认证
05 drf源码剖析之认证 目录 05 drf源码剖析之认证 1. 认证简述 2. 认证的使用 3. 源码剖析 4. 总结 1. 认证简述 当我们通过Web浏览器与API进行交互时,我们可以登录,然后 ...
- CRM开发系列
CRM,客户关系管理系统(Customer Relationship Management).企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销售循环:新客户的 ...
- CRM【第三篇】: crm业务
1. 项目背景 crm系统是某某教育公司正在使用的项目,系统主要为 销售部.运营部.教质部门提供管理平台,随着公司规模的扩展,对公司员工的业务信息量化以及信息化建设越来越重要. crm系统为不同角色的 ...
- linux专题(四):常用的基本命令(二)基本属性
看懂文件属性 Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限.为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定. 在Li ...
- Vue你不得不知道的异步更新机制和nextTick原理
前言 异步更新是 Vue 核心实现之一,在整体流程中充当着 watcher 更新的调度者这一角色.大部分 watcher 更新都会经过它的处理,在适当时机让更新有序的执行.而 nextTick 作为异 ...
- MVC + EFCore 项目实战 - 数仓管理系统4 – 需求分解
上次课程我们完成了项目基本的UI风格配置. 现在就开始进入我们的需求开发,我们先捋一下需求. 一.总体需求说明 项目背景第一篇文章已有介绍,我们回顾一下. 这是一个数据管理"工具类" ...
- Quartz.Net系列(十六):Misfire策略在SimpleScheduler和CronScheduler中的使用
1.场景 ①因为工作线程都在忙碌,所以导致某些Trigger得不到触发 也就是默认10个工作线程而我有15个Trigger同时触发 这就导致有5个不能被触发,而不幸的是Trigger所关联的Job执行 ...
- 洛谷P2365/5785 任务安排 题解 斜率优化DP
任务安排1(小数据):https://www.luogu.com.cn/problem/P2365 任务安排2(大数据):https://www.luogu.com.cn/problem/P5785 ...