【UOJ #29】【IOI 2014】holiday

http://uoj.ac/problem/29

cdq四次处理出一直向左, 一直向右, 向左后回到起点, 向右后回到起点的dp数组,最后统计答案。

举例:$f_i$表示一直向右走i天能参观的最多景点数。

其中有一个很重要的条件$f_i≤f_{i+1}$,这个条件是分治的前提。

关于这个条件的证明,我想了好久才想出来,用反证法证明一下就行。

分治时需要用主席树维护路径上的前k大和。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include"holiday.h"
using namespace std;
typedef long long ll;
const int N = 100003;
const int M = 250003;

struct node {
    int l, r, s;
    ll sum;
    node() {l = r = s = sum = 0;}
} T[N * 20];
int a[N], H[N], cnt = 0, root[N], top, st;
ll f[M], g[M], f1[M], g1[M];

void update(int &pos, int l, int r, int key) {
    T[++cnt] = T[pos]; pos = cnt;
    ++T[pos].s; T[pos].sum += H[key];
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (key <= mid) update(T[pos].l, l, mid, key);
    else update(T[pos].r, mid + 1, r, key);
}

ll query(int tl, int tr, int l, int r, int num) {
    if (l == r) {return min(T[tr].sum - T[tl].sum, 1ll * H[l] * num);}
    int mid = (l + r) >> 1, s = T[T[tr].r].s - T[T[tl].r].s;
    if (s >= num)
        return query(T[tl].r, T[tr].r, mid + 1, r, num);
    else
        return T[T[tr].r].sum - T[T[tl].r].sum + query(T[tl].l, T[tr].l, l, mid, num - s);
}

void cdq_f(int l, int r, int tmp_l, int tmp_r) {
    if (l > r) return;
    int mid = (l + r) >> 1, pos = tmp_l;
    ll t;
    for(int i = tmp_l; i - st <= mid && i <= tmp_r; ++i)
        if ((t = query(root[st - 1], root[i], 1, top, mid - i + st)) > f[mid])
            f[mid] = t, pos = i;
    cdq_f(l, mid - 1, tmp_l, pos);
    cdq_f(mid + 1, r, pos, tmp_r);
}

void cdq_f1(int l, int r, int tmp_l, int tmp_r) {
    if (l > r || tmp_l > tmp_r) return;
    int mid = (l + r) >> 1, pos = tmp_l;
    ll t;
    for(int i = tmp_l; ((i - st) << 1) <= mid && i <= tmp_r; ++i)
        if ((t = query(root[st], root[i], 1, top, mid - ((i - st) << 1))) > f1[mid])
            f1[mid] = t, pos = i;
    cdq_f1(l, mid - 1, tmp_l, pos);
    cdq_f1(mid + 1, r, pos, tmp_r);
}

void cdq_g(int l, int r, int tmp_l, int tmp_r) {
    if (l > r) return;
    int mid = (l + r) >> 1, pos = tmp_r;
    ll t;
    for(int i = tmp_r; st - i <= mid && i >= tmp_l; --i) {
        if ((t = query(root[i - 1], root[st], 1, top, mid - st + i)) > g[mid])
            g[mid] = t, pos = i;
    }
    cdq_g(l, mid - 1, pos, tmp_r);
    cdq_g(mid + 1, r, tmp_l, pos);
}

void cdq_g1(int l, int r, int tmp_l, int tmp_r) {
    if (l > r || tmp_l > tmp_r) return;
    int mid = (l + r) >> 1, pos = tmp_r;
    ll t;
    for(int i = tmp_r; ((st - i) << 1) <= mid && i >= tmp_l; --i) {
        if ((t = query(root[i - 1], root[st - 1], 1, top, mid - ((st - i) << 1))) > g1[mid])
            g1[mid] = t, pos = i;
    }
    cdq_g1(l, mid - 1, pos, tmp_r);
    cdq_g1(mid + 1, r, tmp_l, pos);
}

ll findMaxAttraction(int n, int start, int d, int attraction[]) {
    for(int i = 0; i < n; ++i) H[++cnt] = attraction[i];
    sort(H + 1, H + cnt + 1);
    cnt = unique(H + 1, H + cnt + 1) - H;
    for(int i = 1; i <= n; ++i) a[i] = lower_bound(H + 1, H + cnt, attraction[i - 1]) - H;
    top = cnt - 1; cnt = 0;

    for(int i = 1; i <= n; ++i) {
        root[i] = root[i - 1];
        update(root[i], 1, top, a[i]);
    }

    st = start + 1;
    cdq_f(0, d, st, n);
    cdq_f1(0, d, st + 1, n);
    cdq_g(0, d, 1, st);
    cdq_g1(0, d, 1, st - 1);

    ll ans = max(f[d], g[d]);
    for(int i = 1; i <= d; ++i)
        ans = max(ans, max(f[i] + g1[d - i], g[i] + f1[d - i]));
    return ans;
}

UOJ上是交互题的形式

时间: 2024-10-12 23:30:08

【UOJ #29】【IOI 2014】holiday的相关文章

JAVA 基础编程练习题29 【程序 29 求矩阵对角线之和】

29 [程序 29 求矩阵对角线之和] 题目:求一个 3*3 矩阵对角线元素之和 程序分析:利用双重 for 循环控制输入二维数组,再将 a[i][i]累加后输出. package cskaoyan; public class cskaoyan29 { @org.junit.Test public void diagonal() { java.util.Scanner in = new java.util.Scanner(System.in); int[][] arr = new int[3][

uoj 41 【清华集训2014】矩阵变换 婚姻稳定问题

[清华集训2014]矩阵变换 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/41 Description 给出一个 N 行 M 列的矩阵A, 保证满足以下性质: M>N.    矩阵中每个数都是 [0,N] 中的自然数.    每行中, [1,N] 中每个自然数都恰好出现一次.这意味着每行中 0 恰好出现 M−N 次.    每列中,[1,N] 中每个自然数至多出现一次. 现在我们要在每行中选取一个非零数,

AC日记——【清华集训2014】奇数国 uoj 38

#38. [清华集训2014]奇数国 思路: 题目中的number与product不想冲: 即为number与product互素: 所以,求phi(product)即可: 除一个数等同于在模的意义下乘以一个数的逆元: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 100005

【UOJ】【UR #2】猪猪侠再战括号序列(splay/贪心)

http://uoj.ac/problem/31 纪念伟大的没有调出来的splay... 竟然那个find那里写错了!!!!!!!!!!!!! 以后要记住:一定要好好想过! (正解的话我就不写了,太简单了.. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #

[官方软件] Easy Sysprep v4.3.29.602 【系统封装部署利器】(2016.01.22)--skyfree大神

[官方软件] Easy Sysprep v4.3.29.602 [系统封装部署利器](2016.01.22) Skyfree 发表于 2016-1-22 13:55:55 https://www.itsk.com/forum.php?mod=viewthread&tid=362766&highlight=Easy%2BSysprep [官方软件] Easy Sysprep v4.3.29.602 [系统封装部署利器](2016.01.22) [Easy Sysprep]概述:Easy Sy

【2014】字符串(3)

题目描述(50分): 通过键盘输入100以内正整数的加.减运算式,请编写一个程序输出运算结果字符串. 输入字符串的格式为:"操作数1 运算符 操作数2","操作数"与"运算符"之间以一个空格隔开. 补充说明: 1.操作数为正整数,不需要考虑计算结果溢出的情况. 2.若输入算式格式错误,输出结果为"0". 要求实现函数: void arithmetic(const char *pInputStr, long lInputLen,

【动态规划】【缩点】NCPC 2014 G Outing

题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1793 题目大意: 一辆公交车,上面M个座位,N个人(M<=N<=1000),每个人只有在Ci也上车的情况下才上车.问最多上车几人. 题目思路: [动态规划][缩点] 首先这是一张N个点N条边的有向图.如果J在I也上车的情况下才上车则连一条I到J的边.这样每个点入度最多为1. 这张图有可能有环,所以先缩点,缩完点之后每个环不会有入边,且一定是一个子树的根节点.这样原来的有环的图就变成

【图论】【宽搜】【染色】NCPC 2014 A Ades

题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1787 题目大意: N个点M条无向边(N,M<=200000),一个节点只能有一个标记.每条边有一个值{0,1或2}表示这条边连接的两个节点拥有的标记之和.问只要要多少个标记才能满足,无解impossible. 题目思路: [图论][宽搜] 因为每个点只能有或没有标记.所以可以枚举每个联通块的其中一个点有还是没有标记,用这个点去拓展这个点的联通块并01染色(这个点所能到达的所有点) 初

【2014】 字符串(2)

题目描述(40分): 通过键盘输入一串小写字母(a~z)组成的字符串.请编写一个字符串压缩程序,将字符串中连续出席的重复字母进行压缩,并输出压缩后的字符串. 压缩规则: 1.仅压缩连续重复出现的字符.比如字符串"abcbc"由于无连续重复字符,压缩后的字符串还是"abcbc". 2.压缩字段的格式为"字符重复的次数+字符".例如:字符串"xxxyyyyyyz"压缩后就成为"3x6yz". 要求实现函数: v