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 single file (the existing selection is discarded). A shift-click is used to select a range of files from the file clicked last time to the current file (the existing selection is discarded). Finally, a control-click is used to invert the selection state of a single file. Consider a sequence of actions. First we select file #5 simply by clicking it. Then, shift-clicking file #10 we get the following selection: #5, #6, #7, #8, #9, #10. If after that we control-click files #7 and #3 then we will have files #3, #5, #6, #8, #9, and #10 selected. Shift-clicking file #1 we select files #1, #2, and #3 (last time we clicked file #3, and the previous selection is gone). Vasya is wondering, what the minimum number of clicks will be, to make a certain selection from the list of files. Write a program to determine the optimal way of making the required selection. If there are several minimal solutions, any of them is considered correct. Example. Suppose we are to select files #2, #5, #6, #8, #9 from a list of 10 files. A possible optimal solution will include the following clicks: 5, Shift+9, Ctrl+2, Ctrl+7.

Input

The first line contains an integer n, the number of files in the list. The following line contains n characters defining the required selection. If i-th file is to be selected then there is an asterisk (“*”) in position i, and a dot (“.”) otherwise.

Output

The first line of the output file must contain a single integer k – the minimum number of clicks necessary to make the given selection. The following k lines must define the way to make such a selection. Each line should contain the number of file to be clicked on the corresponding step, and a prefix “Ctrl+” or “Shift+” (without quotation marks) where necessary.

Sample Input

10 .*..**.**.

Sample Output

4 5 Shift+9 Ctrl+2 Ctrl+7

HINT

1 ≤ n ≤ 105 .

题意

有3种操作

1.光标移动到X

2.shift X,直接选择从光标到X的位置

3.选择X,如果X已经被选中,那就取消X的选中状态

问你最少多少步,可以选择所有的*

并且把步骤输出

题解:

注意,只能shift 1次,所以直接扫一遍就好了

跑一遍线段树维护的DP,表示到这儿,所需要的最小代价是多少

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <queue>
#include <iomanip>
#include <string>
#include <ctime>
#include <list>
#include <bitset>
typedef unsigned char byte;
#define pb push_back
#define input_fast std::ios::sync_with_stdio(false);std::cin.tie(0)
#define local freopen("in.txt","r",stdin)
#define pi acos(-1)

using namespace std;
const int maxn = 1e5 + 500;
char str[maxn];
int length , sum[maxn], dp[maxn];
vector<int>Q,Q2;

struct operation
{
    int x;
    int type;
};

operation nxt[maxn];

struct QueryData
{
    int minv , minpos;
    QueryData(int minv , int minpos)
    {
        this->minv = minv , this->minpos = minpos;
    }
};

typedef int SgTreeDataType;
struct treenode
{
  int L , R  ;
  SgTreeDataType minv , minpos;
  void updata(SgTreeDataType v)
  {
        minv = v;
  }
};

treenode tree[maxn * 4];

inline void push_up(int o)
{
    if(tree[o*2].minv > tree[o*2+1].minv)
    {
        tree[o].minv = tree[o*2+1].minv;
        tree[o].minpos = tree[o*2+1].minpos;
    }
    else
    {
        tree[o].minv = tree[o*2].minv;
        tree[o].minpos = tree[o*2].minpos;
    }
}

inline void build_tree(int L , int R , int o)
{
    tree[o].L = L , tree[o].R = R,tree[o].minv = 1<<29 , tree[o].minpos = 0;
    if(L == R) tree[o].minpos = L;
    if (R > L)
    {
        int mid = (L+R) >> 1;
        build_tree(L,mid,o*2);
        build_tree(mid+1,R,o*2+1);
    }
}

inline void updata(int QL,int QR,SgTreeDataType v,int o)
{
    int L = tree[o].L , R = tree[o].R;
    if (QL <= L && R <= QR) tree[o].updata(v);
    else
    {
        int mid = (L+R)>>1;
        if (QL <= mid) updata(QL,QR,v,o*2);
        if (QR >  mid) updata(QL,QR,v,o*2+1);
        push_up(o);
    }
}

inline QueryData query(int QL,int QR,int o)
{
    int L = tree[o].L , R = tree[o].R;
    if (QL <= L && R <= QR) return QueryData(tree[o].minv,tree[o].minpos);
    else
    {
        int mid = (L+R)>>1;
        if (QL <= mid && QR > mid)
        {
            QueryData a = query(QL,QR,2*o);
            QueryData b = query(QL,QR,2*o+1);
            if(a.minv < b.minv) return a;
            else return b;
        }
        else if (QL <= mid) return query(QL,QR,2*o);
        else return query(QL,QR,2*o+1);
    }
}

void initiation()
{
    memset( dp , 0 , sizeof(dp));
    scanf("%d%s",&length,str+1);sum[0] = 0;
    for(int i = 1 ; i <= length ; ++ i)
    {
        sum[i] = sum[i-1];
        if(str[i] == ‘*‘)
        {
            Q.push_back(i);
        }
        else
        {
            sum[i] ++ ;
            Q2.push_back(i);
        }
    }
}

void solve()
{
    int sz = Q.size();
    int ansL = Q[0],ansR = Q[0],ans=sz;
    build_tree( 0 , sz - 1 , 1 );
    for(int i = 0 ; i < sz ; ++ i) updata( i , i , sum[Q[i]] - i , 1);
    for(int i = 0 ; i < sz - 1; ++ i)
    {
        QueryData y = query( i + 1 , sz  - 1, 1);
        int newans = i + 1 + sz + y.minv - sum[Q[i]];
        if(newans < ans)
        {
            ans = newans;
            ansL = i;
            ansR = y.minpos;
        }
    }
    printf("%d\n",ans);
    if(ansL != ansR)
    {
        printf("%d\n",Q[ansL]);
        printf("Shift+%d\n",Q[ansR]);
        for(int i = 0 ; i < sz ; ++ i) if(i < ansL || i > ansR) printf("Ctrl+%d\n",Q[i]);
        for(int i = 0 ; i < Q2.size() ; ++ i) if( Q2[i] < Q[ansR] && Q2[i] > Q[ansL]) printf("Ctrl+%d\n",Q2[i]);
    }
    else
    {
        printf("%d\n",Q[0]);
        for(int i = 1 ; i < sz ; ++ i) printf("Ctrl+%d\n",Q[i]);
    }
}

int main(int argc,char *argv[])
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    initiation();
    if(Q.size() == 0) printf("0\n");
    else if(Q.size() == 1) printf("1\n%d\n",Q[0]);
    else if(Q.size() == 2) printf("2\n%d\nCtrl+%d\n",Q[0],Q[1]);
    else solve();
    return 0;
}
时间: 2024-10-05 23:37:07

Codeforces GYM 100114 D. Selection 线段树维护DP的相关文章

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) =

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

Codeforces Gym 100114 H. Milestones 离线树状数组

H. Milestones Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description The longest road of the Fairy Kingdom has n milestones. A long-established tradition defines a specific color for milestones in each region, with a

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

CodeForces 343D 线段树维护dfs序

给定一棵树,初始时树为空 操作1,往某个结点注水,那么该结点的子树都注满了水 操作2,将某个结点的水放空,那么该结点的父亲的水也就放空了 操作3,询问某个点是否有水 我们将树进行dfs, 生成in[u], 访问结点u的时间戳,out[u],离开结点u的时间戳 每个结点的in值对应在线段树中的区间的一点 那么对于操作1, 只要将区间[in[u],out[u]] 的值都改为1, 但是如果区间[in[u],out[u]] 原先存在为0的点,那么父区间肯定是空的,这个操作不能 改变父区间的状态,所以需要

codeforces 487B B. Strip(rmq+线段树+二分)

题目链接: codeforces 487B 题目大意: 给出一个序列,要把序列划分成段,每一段最少有L个元素,段中的最大元素和最小元素之差不大于s,问划分的段的最少的数量是多少. 题目分析: 首先用rmq维护区间最大值和区间最小值. 然后按顺序扫描数组,线段树维护的数组,每个记录当前点作为最后一个点的前i个点划分的最小的段数,那么每次更新就是二分找到可以转移到我的最远距离,然后再选取与我距离大于l的那部分,取最小值即可. 最终结果就是线段树维护的数组的最后一个位置的元素的值. AC代码: #in

cf213E 线段树维护hash

链接 https://codeforces.com/contest/213/problem/E 题目大意 给出两个排列a.b,长度分别为n.m,你需要计算有多少个x,使 得\(a_1 + x; a_2 + x; a_3 + x... a_n + x\) 是b 的子序列(不连续的那种). 思路 巧妙啊 暴力直接扫会T 我们构造一个c数组,使得c[b[i]]=i 这样x+1到x+1+n就是一段连续的区间了233 插回去看看他们相对大小是不是和a数组相同 因为不连续所以线段树维护hash值,线段树按照