Codeforces 750E New Year and Old Subsequence 线段树 + dp (看题解)

New Year and Old Subsequence

第一感觉是离线之后分治求dp, 但是感觉如果要把左边的dp值和右边的dp值合起来, 感觉很麻烦而且时间复杂度不怎么对。。

然后就gun取看题解了, 用线段树维护dp的值, 然后区间合并求答案。 每个节点保存dp[ i ][ j ]表示, 把当前管理的区间删到

s{2017}中的 s[ i + 1 ] - s[ j - 1 ],最少删几个, 然后合并的时候5 ^ 3合并。

#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0);

using namespace std;

const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T& a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T& a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T& a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T& a, S b) {return a > b ? a = b, true : false;}

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
struct info {
    int a[5][5];
    info() {
        memset(a, inf, sizeof(a));
    }
    void go(char c) {
        for(int i = 0; i < 5; i++)
            for(int j = 0; j < 5; j++)
                a[i][j] = i == j ? 0 : inf;
        if(c == ‘2‘) a[0][0] = 1, a[0][1] = 0;
        else if(c == ‘0‘) a[1][1] = 1, a[1][2] = 0;
        else if(c == ‘1‘) a[2][2] = 1, a[2][3] = 0;
        else if(c == ‘7‘) a[3][3] = 1, a[3][4] = 0;
        else if(c == ‘6‘) a[3][3] = 1, a[4][4] = 1;
    }
};
info operator + (const info& x, const info& y) {
    info z;
    for(int i = 0; i < 5; i++)
        for(int j = i; j < 5; j++)
            for(int k = i; k < 5; k++)
                chkmin(z.a[i][j], x.a[i][k] + y.a[k][j]);
    return z;
}
info a[N << 2];
void build(char* s, int l, int r, int rt) {
    if(l == r) {
        a[rt].go(s[l]);
        return;
    }
    int mid = l + r >> 1;
    build(s, lson); build(s, rson);
    a[rt] = a[rt << 1] + a[rt << 1 | 1];
}
info query(int L, int R, int l, int r, int rt) {
    if(L <= l && r <= R) return a[rt];
    int mid = l + r >> 1;
    if(R <= mid) return query(L, R, lson);
    else if(L > mid) return query(L, R, rson);
    else return query(L, R, lson) + query(L, R, rson);
}

int n, q;
char s[N];

int main() {
    scanf("%d%d", &n, &q);
    scanf("%s", s + 1);
    build(s, 1, n, 1);
    while(q--) {
        int L, R;
        scanf("%d%d", &L, &R);
        info ans = query(L, R, 1, n, 1);
        printf("%d\n", ans.a[0][4] == inf ? -1 : ans.a[0][4]);
    }
    return 0;
}

/*
*/

原文地址:https://www.cnblogs.com/CJLHY/p/10746369.html

时间: 2024-08-30 05:56:15

Codeforces 750E New Year and Old Subsequence 线段树 + dp (看题解)的相关文章

Codeforces 750E New Year and Old Subsequence - 线段树 - 动态规划

A string t is called nice if a string "2017" occurs in t as a subsequence but a string "2016" doesn't occur in t as a subsequence. For example, strings "203434107" and "9220617" are nice, while strings "20016&q

Codeforces 283E Cow Tennis Tournament 线段树 (看题解)

Cow Tennis Tournament 感觉这题的难点在于想到求违反条件的三元组.. 为什么在自己想的时候没有想到求反面呢!!!! 违反的三元组肯定存在一个人能打败其他两个人, 扫描的过程中用线段树维护一下就好了. 反思: 计数问题: 正难则反 正难则反 正难则反 !!!! #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #define fi

Codeforces 1076G Array Game 博弈 + 线段树 (看题解)

Array Game 考虑最裸的dp去求胜负态. dp[ i ] 从后面的 m 个状态转移过来. 我们考虑如何用线段树维护, T[ i ][ mask ] 表示 i 这段区间如果后面接的m位是mask使时开头m位的mask, 对于修改的话只要维护一个反过来的T2就可以了. 感觉是可以想出来的题, 为什么没想出来啊啊啊. #include<bits/stdc++.h> #define LL long long using namespace std; const int N = (int)2e5

HDU - 5770 Treasure 思维 + 扫描线 + 线段树 (看题解)

HDU - 5770 没想出来, 感觉不应该啊, 没有想到转换成二维上的点的问题. 对于对钥匙和宝藏(u, v), 如果lca != u && lca != v 那么起点从u子树出发, 终点在v子树就能得到贡献. 子树在dfs序下是连续一段, 所以就对应到二维平面一个矩形加上一个数值, 求值最大的点. 对于lca == u || lca == v同样可以讨论出来. 还有一种情况就是u == v, 我们先把贡献都加上, 然后对于不经过u 的所有路径进去这个贡献. 然后扫描线扫一遍就好了. #

CodeForces 52C Circular RMQ(区间循环线段树,区间更新,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are given circular array a0,?a1,?...,?an?-?1. There are two types of operations with it: inc(lf,?rg,?v) - this operation increases each element on the segm

codeforces 446C DZY Loves Fibonacci Numbers 数论+线段树成段更新

DZY Loves Fibonacci Numbers Time Limit:4000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2014-07-14) Description In mathematical terms, the sequence Fn of Fibonacci numbers is defi

Codeforces Beta Round #12 D. Ball (线段树)

题目大意: n个女性中,如果有一个女性的三维比这个女性的三维都大,这个女性就要自杀.问要自杀多少个. 思路分析: 先按照第一维排序. 然后离散化第二维,用第二维的下标建树,树上的值是第三维,更新的时候取最大值. 注意是按照第一维度从大到小进入线段树. 而且还要严格递增. 所以处理第一维度比较大小的时候要分开处理,要把相同的先判断,再更新入树. 那么如何判断这个女性是否自杀. 首先,你知道第一维度是从大到小的,所以先进树了的节点的第一维度一定更大. 再次,我们考虑第二维度,我们去树上第二维度下标大

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu