zoukankan      html  css  js  c++  java
  • [BZOJ2138]stone(Hall定理,线段树)

    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分钟最多能取多少个石子。
    (nle 40000)

    Solution

    Hall定理:

    设二分图中( ext{G=< V1,V2,E >})( ext{|V1|=m<=|V2|=n}) ,( ext{G})中存在从 ( ext{V1})( ext{V2})的完全匹配当且仅当( ext{V1})中任意( ext{k(k=1,2,...,m)})个顶点至少与( ext{V2})( ext{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 ]

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

    [iin [0, l - 1], jin [r + 1, n]\ f[j] - k - g[i] geq 0\ kle min(f[j]) - max(g[i])\ kle 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
    ", 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;
    }
    
  • 相关阅读:
    Mysql上手
    Markdown精简版个人语法
    Sublime Text3插件管理
    Eclipse的快捷键
    使用github page 页面建博客中遇到的几个小问题
    2015 圣诞 限免软件分享
    啦啦啦-根据关键字进行字符串拷贝
    使用 sprintf swprintf 函数进行 unicode 与 ANSI 编码的转换
    c++11: 用户定义字面量
    通过模板获取数组长度
  • 原文地址:https://www.cnblogs.com/cnyali-Tea/p/11439760.html
Copyright © 2011-2022 走看看