codeforces Good bye 2016 E 线段树维护dp区间合并

题目大意:给你一个字符串,范围为‘0’~‘9‘,定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问,最少删除多少个字符,使得串中符合ugly串?

思路:定义dp(i, j),其中i=5,j=5,因为只需要删除2016当中其中一个即可,所以一共所需要删除的字符和需要的字符为20176,因此i和j只要5就够了。

然后转移就是dp(i,i) = 0, 如果说区间大小为1的话,那么如果是2017中的一个,那么就是dp(pos, pos+1) = 0, dp(pos,pos) = 1。但是如果pos为‘6‘这个字符,那么定义6这个pos=x转移就要为dp[x-1][x-1] = dp[x][x] = 1.然后我们再去转移就好了。

然后最后一定要注意线段树的合并顺序哦

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
/*
定义dp(x,i,j)表示区间[0,x)内能够获得的2017的前缀长度为i的,至少要删除多少个字符
最少要删除多少个才能构成前缀为j的2017,然后我们不需要去管x,只需要用线段树去维护i,j即可。
*/
const int inf = 0x3f3f3f3f;
const int maxn = 200000 + 5;
struct Node{
    int dp[5][5];
}tree[maxn << 2];
int n, q;
char ch[maxn];
map<char, int> id;

inline void init(Node &t){
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++)
            t.dp[i][j] = inf;
}

Node Merge(Node a, Node b){
    Node tmp; init(tmp);
    for (int i = 0; i <= 4; i++)
        for (int j = i; j <= 4; j++)
            for (int k = i; k <= j; k++)
                tmp.dp[i][j] = min(tmp.dp[i][j], a.dp[i][k] + b.dp[k][j]);
    return tmp;
}

void display(Node t){
    for (int i = 0; i < 5; i++){
        for (int j = 0; j < 5; j++)
            printf("%d ", t.dp[i][j]);
        cout << endl;
    }
}

void buildtree(int l, int r, int o){
    if (l == r){
        init(tree[o]);
        for (int i = 0; i < 5; i++)
            tree[o].dp[i][i] = 0;
        int pos = id[ch[l]];
        if (pos <= 3) tree[o].dp[pos][pos] = 1, tree[o].dp[pos][pos + 1] = 0;
        if (pos == 4) tree[o].dp[3][3] = tree[o].dp[4][4] = 1;
        return ;
    }
    int mid = (l + r) / 2;
    if (l <= mid) buildtree(l, mid, o << 1);
    if (r > mid) buildtree(mid + 1, r, o << 1 | 1);
    tree[o] = Merge(tree[o << 1], tree[o << 1 | 1]);
    //display(tree[o]);
}

Node ans;
void query(int ql, int qr, int l, int r, int o){
    if (ql <= l && qr >= r) {
        if (ql == l) ans = tree[o];
        else ans = Merge(ans, tree[o]);
        return ;
    }
    int mid = (l + r) / 2;
    if (ql <= mid) query(ql, qr, l, mid, o << 1);
    if (qr > mid) query(ql, qr, mid + 1, r, o << 1 | 1);
}

int main(){
    int cnt = 4;
    id[‘2‘] = 0, id[‘0‘] = 1, id[‘1‘] = 2, id[‘7‘] = 3, id[‘6‘] = 4;
    for (char i = ‘3‘; i <= ‘9‘; i++) if(id.count(i) == 0) id[i] = ++cnt;
    cin >> n >> q;
    scanf("%s", ch + 1);
    buildtree(1, n, 1);
    for (int i = 1; i <= q; i++){
        int ql, qr; scanf("%d%d", &ql, &qr);
        init(ans);
        query(ql, qr, 1, n, 1);
        if (ans.dp[0][4] >= inf) puts("-1");
        else printf("%d\n", ans.dp[0][4]);
    }
    return 0;
}

关键:线段树合并

时间: 2024-10-29 19:08:45

codeforces Good bye 2016 E 线段树维护dp区间合并的相关文章

Codeforces GYM 100114 D. Selection 线段树维护DP

D. Selection Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description When selecting files in an application dialog, Vasya noted that he can get the same selection in different ways. A simple mouse click selects a sing

hdu 3308 线段树单点更新 区间合并

http://acm.hdu.edu.cn/showproblem.php?pid=3308 学到两点: 1.以区间端点为开始/结束的最长......似乎在Dp也常用这种思想 2.分类的时候,明确标准逐层分类,思维格式: 条件一成立: { 条件二成立: { } else { } } else { 条件二成立: { } else { } } 上面的这种方式很清晰,如果直接想到那种情况iif(条件一 &条件二)就写,很容易出错而且把自己搞乱,或者情况不全,,,我就因为这WA了几次 3.WA了之后 ,

codeforces CF718C Sasha and Array 线段树维护矩阵

$ \Rightarrow $ 戳我进CF原题 C. Underground Lab time limit per test: 1 second memory limit per test: 256 megabytes input: standard input output: standard output The evil Bumbershoot corporation produces clones for gruesome experiments in a vast undergroun

2016shenyang-1002-HDU5893-List wants to travel-树链剖分+线段树维护不同区间段个数

肯定先无脑树链剖分,然后线段树维护一段区间不同个数,再维护一个左右端点的费用. 线段树更新,pushDown,pushUp的时候要注意考虑链接位置的费用是否相同 还有就是树链剖分操作的时候,维护上一个更新的位置的费用. 总之就是出现区间合并,就考虑总数是否要减一 好想不好写 //场上根本写不完啊 1 /*--------------------------------------------------------------------------------------*/ 2 3 #inc

HDU 3308 LCIS (线段树&#183;单点更新&#183;区间合并)

题意  给你一个数组  有更新值和查询两种操作  对于每次查询  输出对应区间的最长连续递增子序列的长度 基础的线段树区间合并  线段树维护三个值  对应区间的LCIS长度(lcis)  对应区间以左端点为起点的LCIS长度(lle)  对应区间以右端点为终点的LCIS长度(lri)  然后用val存储数组对应位置的值  当val[mid + 1] > val[mid] 的时候就要进行区间合并操作了 #include <cstdio> #include <algorithm>

Codeforces Round #426 (Div. 2) D. The Bakery(线段树维护dp)

题目链接: Codeforces Round #426 (Div. 2) D. The Bakery 题意: 给你n个数,划分为k段,每段的价值为这一段不同的数的个数,问如何划分,使得价值最大. 题解: 考虑dp[i][j]表示划分为前j个数划分为i段的最大价值,那么这就是一个n*n*k的dp, 考虑转移方程dp[i][j]=max{dp[i][k]+val[k+1][j]},我们用线段树去维护这个max,线段树上每个节点维护的值是dp[i][k]+val[k+1][j],对于每加进来的一个数a

Codeforces 777D Hanoi Factory(线段树维护DP)

题目链接 Hanoi Factory 很容易想到这是一个DAG模型,那么状态转移方程就出来了. 但是排序的时候有个小细节:b相同时看a的值. 因为按照惯例,堆塔的时候肯定是内半径大的在下面. 因为N有1e5,那么DP的时候用线段树优化一下,就可以了. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for(int i(a); i <= (b); ++i) 6 7 typedef lo

HDU 4521 小明系列问题——小明序列 (线段树维护DP)

题目地址:HDU 4521 基本思路是DP.找前面数的最大值时能够用线段树来维护节省时间. 因为间隔要大于d. 所以能够用一个队列来延迟更新,来保证每次询问到的都是d个之前的. 代码例如以下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include

(线段树) (h) poj3667 (区间合并)

poj3667 Hotel 如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍 [题目大意] 有一个旅馆,有N个房间排成一排,现在有两种操作,第一是有X个顾客要入住连续的X个房间, 要求输出最小的左端点的位置,不能满足就输出0,第二是将以L开始,长度为X的连续房间清空. [输入文件] 第一行两个数N,M,表示房间数和操作数 接下来M行,每行有两种情况: 1 X 表示操作1 2 L X 表示操作2 [输出文件] 对于每一个1操作,输出答案. 题即为求最靠左的连续区间并置满以及把一段