Description

话说Nan在海边等人,预计还要等上M分钟。为了打发时间,他玩起了石子。Nan搬来了N堆石子,编号为1到N,每堆

包含Ai颗石子。每1分钟,Nan会在编号在\([L_i,R_i]\)之间的石堆中挑出任意Ki颗扔向大海(好疼的玩法),如果\([L_i,R_i]\)剩下石子不够\(K_i\)颗,则取尽量地多。为了保留扔石子的新鲜感,Nan保证任意两个区间\([L_i,R_i]\)和\([L_j,R_j]\),不会存在\(L_i<=L_j\& R_j<=R_i\)的情况,即任意两段区间不存在包含关系。可是,如果选择不当,可能无法扔出最多的石子,这时Nan就会不高兴了。所以他希望制定一个计划,他告诉你他m分钟打算扔的区间\([L_i,R_i]\)以及\(K_i\)。现在他想你告诉他,在满足前i-1分钟都取到你回答的颗数的情况下,第i分钟最多能取多少个石子。

\(n\le 40000\)

Solution

Hall定理:

设二分图中\(\text{G=< V1,V2,E >}\)中 \(\text{|V1|=m<=|V2|=n}\) ,\(\text{G}\)中存在从 \(\text{V1}\)到\(\text{V2}\)的完全匹配当且仅当\(\text{V1}\)中任意\(\text{k(k=1,2,...,m)}\)个顶点至少与\(\text{V2}\)中\(\text{k}\)个顶点是相邻的。

建立二分图匹配模型,左部节点为石头(每个点拆为A[i]个点),右部节点为需求(每个点拆为K[i]个点),从小到大依次加入右部节点,然后询问在之前需求点匹配数不变的情况下该点最多能匹配的点数。

不难发现最后是一部分完美匹配+一部分不完美匹配,根据hall定理,一些点具有完美匹配要求任取左部区间,该区间石头数大于等于包含在区间内的需求数。

那么设 \(a[i]\) 表示右端点 \(\le i\) 的区间需求量, \(b[i]\) 为左端点 \(\le i\) 的区间的需求量。

那么区间 \([l,r]\) :

石子数 \(=s[r] - s[l - 1]\)

需求数 \(=a[r] - b[l - 1]\)

那么只要满足 \(\forall 0 \le i < j \le n\),

\[(s[j] - s[i]) - (a[j] - b[i]) \geq 0
\]

设 \(f[i] = s[i] - a[i], g[i] = s[i] - b[i]\)。

考虑区间 \([l,r]\) 满足 \(k\) 的需求量造成的影响。

\[f[r...n] -= k\\
g[l...n] -= k
\]

接下来分类讨论,列出不等式:

\[i\in [0, l - 1], j\in [r + 1, n]\\
f[j] - k - g[i] \geq 0\\
k\le min(f[j]) - max(g[i])\\
k\le min(f[r+1...n]) - max(g[1...l-1])
\]

当 \(k\) 取 \(min(f[r+1...n]) - max(g[0...l-1])\) 时最优。

用线段树维护即可。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream> typedef long long LL;
typedef unsigned long long uLL; #define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
#define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i)) using namespace std; inline void proc_status()
{
ifstream t("/proc/self/status");
cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read()
{
register int x = 0; register int f = 1; register char c;
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
return x * f;
}
template<class T> inline void write(T x)
{
static char stk[30]; static int top = 0;
if (x < 0) { x = -x, putchar('-'); }
while (stk[++top] = x % 10 xor 48, x /= 10, x);
while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; } const int maxN = 4e4; int n, m;
int s[maxN + 2], K[maxN + 2]; #define ls (x << 1)
#define rs (x << 1 | 1)
#define Rson rs, mid + 1, r
#define Lson ls, l, mid
int f[maxN << 2], g[maxN << 2], tagG[maxN << 2], tagF[maxN << 2]; void pushup(int x)
{
f[x] = min(f[ls], f[rs]);
g[x] = max(g[ls], g[rs]);
} void build(int x, int l, int r)
{
if (l == r) { f[x] = g[x] = s[l]; return; }
int mid = l + r >> 1;
build(Lson); build(Rson);
pushup(x);
} void pushG(int x, int k)
{
tagG[x] += k;
g[x] += k;
} void pushF(int x, int k)
{
tagF[x] += k;
f[x] += k;
} void pushdown(int x)
{
if (tagG[x] != 0)
{
pushG(ls, tagG[x]);
pushG(rs, tagG[x]);
tagG[x] = 0;
}
if (tagF[x] != 0)
{
pushF(ls, tagF[x]);
pushF(rs, tagF[x]);
tagF[x] = 0;
}
} void addF(int x, int l, int r, int L, int R, int k)
{
if (L <= l and r <= R) { return pushF(x, k); }
int mid = l + r >> 1;
pushdown(x);
if (L <= mid) addF(Lson, L, R, k);
if (R > mid) addF(Rson, L, R, k);
pushup(x);
} void addG(int x, int l, int r, int L, int R, int k)
{
if (L <= l and r <= R) { return pushG(x, k); }
int mid = l + r >> 1;
pushdown(x);
if (L <= mid) addG(Lson, L, R, k);
if (R > mid) addG(Rson, L, R, k);
pushup(x);
} int queryG(int x, int l, int r, int L, int R)
{
if (L <= l and r <= R) { return g[x]; }
int mid = l + r >> 1;
pushdown(x);
int ans = -0x3f3f3f3f;
if (L <= mid) chkmax(ans, queryG(Lson, L, R));
if (mid < R) chkmax(ans, queryG(Rson, L, R));
return ans;
} int queryF(int x, int l, int r, int L, int R)
{
if (L <= l and r <= R) { return f[x]; }
int mid = l + r >> 1;
pushdown(x);
int ans = 0x3f3f3f3f;
if (L <= mid) chkmin(ans, queryF(Lson, L, R));
if (mid < R) chkmin(ans, queryF(Rson, L, R));
return ans;
} int main()
{
#ifndef ONLINE_JUDGE
freopen("stone.in", "r", stdin);
freopen("stone.out", "w", stdout);
#endif
int x, y, z, P;
scanf("%d", &n);
scanf("%d%d%d%d", &x, &y, &z, &P);
for (int i = 1; i <= n; ++i)
s[i] = s[i - 1] + ((i - x) * (i - x) + (i - y) * (i - y) + (i - z) * (i - z)) % P;
scanf("%d", &m);
scanf("%d%d%d%d%d%d", &K[1], &K[2], &x, &y, &z, &P);
for (int i = 3; i <= m; ++i)
K[i] = (1ll * x * K[i - 1] + 1ll * y * K[i - 2] + z) % P;
build(1, 0, n);
for (int i = 1; i <= m; ++i)
{
int L, R, k;
scanf("%d%d", &L, &R);
printf("%d\n", k = min(queryF(1, 0, n, R, n) - queryG(1, 0, n, 0, L - 1), K[i]));
addF(1, 0, n, R, n, -k);
addG(1, 0, n, L, n, -k);
}
return 0;
}

[BZOJ2138]stone(Hall定理,线段树)的更多相关文章

  1. 【BZOJ2138】stone Hall定理+线段树

    [BZOJ2138]stone Description 话说Nan在海边等人,预计还要等上M分钟.为了打发时间,他玩起了石子.Nan搬来了N堆石子,编号为1到N,每堆包含Ai颗石子.每1分钟,Nan会 ...

  2. [BZOJ2138]stone[霍尔定理+线段树]

    题意 一共有 \(n\) 堆石子,每堆石子有一个数量 \(a\) ,你要进行 \(m\) 次操作,每次操作你可以在满足前 \(i-1\) 次操作的回答的基础上选择在 \([L_i,R_i]\) 区间中 ...

  3. BZOJ.3693.圆桌会议(Hall定理 线段树)

    题目链接 先考虑链.题目相当于求是否存在完备匹配.那么由Hall定理,对于任意一个区间[L,R],都要满足[li,ri]完全在[L,R]中的ai之和sum小于等于总位置数,即R-L+1.(其实用不到H ...

  4. LOJ.6062.[2017山东一轮集训]Pair(Hall定理 线段树)

    题目链接 首先Bi之间的大小关系没用,先对它排序,假设从小到大排 那么每个Ai所能匹配的Bi就是一个B[]的后缀 把一个B[]后缀的匹配看做一条边的覆盖,设Xi为Bi被覆盖的次数 容易想到 对于每个i ...

  5. loj#6062. 「2017 山东一轮集训 Day2」Pair hall定理+线段树

    题意:给出一个长度为 n的数列 a和一个长度为 m 的数列 b,求 a有多少个长度为 m的连续子数列能与 b匹配.两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当 ...

  6. BZOJ3693: 圆桌会议(Hall定理 线段树)

    题意 题目链接 Sol 好的又是神仙题... 我的思路:对于区间分两种情况讨论,一种是完全包含,另一种是部分包含.第一种情况非常好判断,至于计算对于一个区间[l, r]的$\sum a[i]$就可以了 ...

  7. 模拟赛 怨灵退治 题解(Hall定理+线段树)

    题意: 有 n 群怨灵排成一排,燐每秒钟会选择一段区间,消灭至多 k 只怨灵. 如果怨灵数量不足 k,则会消灭尽量多的怨灵. 燐作为一只有特点的猫,它选择的区间是不会相互包含的.它想要知道它每秒最多能 ...

  8. Codeforces 338E - Optimize!(Hall 定理+线段树)

    题面传送门 首先 \(b_i\) 的顺序肯定不会影响匹配,故我们可以直接将 \(b\) 数组从小到大排个序. 我们考虑分析一下什么样的长度为 \(m\) 的数组 \(a_1,a_2,\dots,a_m ...

  9. BZOJ1135 LYZ(POI2009) Hall定理+线段树

    做这个题之前首先要了解判定二分图有没有完备匹配的Hall定理: 那么根据Hell定理,如果任何一个X子集都能连大于等于|S|的Y子集就可以获得完备匹配,那么就是: 题目变成只要不满足上面这个条件就能得 ...

随机推荐

  1. python学习笔记(9):容器

    一.容器 0.判断所有的容器是否为空,用if not xxx(容器名):来进行判断 1.list 列表 序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一 ...

  2. 解决Vscode编辑器不能打开多标签页问题

    问题描述:编辑代码时,初用vscode,不能打开多个文件:每打开一个文件,都会替换前面一个文件标签,很不方便切换编码: 想要的效果: 解决方式: 方法一: 找到setting.json文件,最外层花括 ...

  3. Nginx针对前端静态资源的缓存处理

    当用户上报一个线上的bug后,开发者修改前端代码的bug上新后,用户反映问题依旧存在的问题...这种情况是不是曾经遇到过,这个问题跟浏览器的缓存机制有很大关系(强制缓存和协商缓存,这里我就不介绍具体的 ...

  4. sqoop使用中文手册

    文章转载自:http://www.zihou.me/html/2014/01/28/9114.html 1.     概述 本文档主要对SQOOP的使用进行了说明,参考内容主要来自于Cloudera ...

  5. 关于使用itext转Html为pdf添加css样式的问题

    使用的jar文件 xmlworker-5.5.11.jar itextpdf-5.5.11.jar 下载地址:https://pan.baidu.com/s/1i5AIBvZ 以下为测试代码 pack ...

  6. vue报错TypeError: Cannot read property 'protocol' of undefined

    错误信息如下所示: isURLSameOrigin.js?3934:57 Uncaught (in promise) TypeError: Cannot read property 'protocol ...

  7. 如何在Linux下手动编译安装gcc

    如果可以通过apt来安装的话,尽量不要手工编译了,手工编译是最后的选择.用apt安装,只需要输入一条命令: sudo apt-get install gcc 手工编译的话,gcc和其他软件包存在如下的 ...

  8. CentOS7修改为国内yum源

    备份源yum源 如果是国内下载的CentOS很可能国内YUM源已经设置好了. 备份/etc/yum.repos.d/下的*.repo文件. 在CentOS中配置使用网易和阿里的开源镜像 wget ht ...

  9. python的list内存分配算法

    前提:python为了提高效率会为list预先分配一定的内存空间供其使用,避免在每次append等操作都去申请内存,下面简单分析下list的内存分配算法,主要就是两段. 1.当没有元素时,newsiz ...

  10. MongoDB的应用

    一.MongoDB后台管理 # ./mongo MongoDB shell version v3.4.2 connecting to: mongodb://127.0.0.1:27017 MongoD ...