虽然每次给一个区间,但是可以看作在区间内进行数个点操作,同样数列下标是动态变化的,如果我们将每个字符出现看作1,被删除看作0,则通过统计前缀和就能轻松计算出两个端点的位置了!这正是经典的树状数组操作

需要注意的是,即使每次找到了两个端点,如果只是朴素总左到右遍历会TLE,所以可以给每个字符开一个set,每个set储存字符出现的位置。

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>
#pragma warning ( disable : 4996 )
using namespace std; int Max( int a, int b ) { return a>b?a:b; }
int Min( int a, int b ) { return a>b?b:a; }
int lowbit( int x ) { return x&-x; } const int inf = 0x3f3f3f3f;
const int maxn = 2e5+; set<int> pos[];
set<int>::iterator it, it2;
char str[maxn];
int istr[maxn];
bool isdel[maxn]; int len, q; int getI( char c )
{
if( c >= 'a' && c <= 'z' ) return c-'a';
if( c >= 'A' && c <= 'Z' ) return c-'A'+;
if( c >= '' && c <= '' ) return c-''+;
} int sum( int x )
{
int ret = ;
while( x > )
{ ret += istr[x]; x -= lowbit(x); }
return ret;
} void add( int x, int d )
{
while( x <= len )
{ istr[x] += d; x += lowbit(x); }
} int find( int s )
{
int mid, lhs = , rhs = len;
int tmp;
while ( lhs <= rhs )
{
mid = (lhs+rhs)>>;
tmp = sum(mid);
if ( tmp >= s ) rhs = mid-;
else lhs = mid+;
}
return lhs;
} void change( int l, int r, char c )
{
int p = getI(c);
it = pos[p].lower_bound(l);
while ( it!=pos[p].end() && *it<=r )
{
isdel[(*it)] = true;
add((*it), -);
it2 = it; it++;
pos[p].erase(it2);
}
} int main()
{
cin >> len >> q;
scanf("%s", str+); for ( int i = ; i <= len; i++ )
{
add(i,);
int p = getI(str[i]);
pos[p].insert(i);
} int x, y;
char c;
for ( int i = ; i <= q; i++ )
{
scanf( "%d %d %c", &x, &y, &c );
x = find(x); y = find(y);
change( x, y, c );
} for( int i = ; i <= len; i++ )
if( !isdel[i] )
printf( "%c", str[i] ); printf("\n");
return ;
}

我们看到区间操作,自然就会想到线段树,但是数列下标是动态变换的,乍一看似乎线段树就没有用武之地了。实际仔细想想,线段树也可以动态统计从哪个区间开始操作,相比普通线段树只是在每次操作前要计算新的下标到哪了,实际上每个节点管辖的范围是始终不变的,变的只是范围内字符的个数size,每次操作前通过这个size找到对应的左端点和右端点就行了。

 //线段树
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#pragma warning ( disable : 4996 )
using namespace std; int Max( int a, int b ) { return a>b?a:b; }
int Min( int a, int b ) { return a>b?b:a; } const int inf = 0x3f3f3f3f;
const int maxn = 2e5+; struct tree {
int size, lhs, rhs;
int clu[];
}t[maxn<<];
char str[maxn]; int getI( char c )
{
if( c >= 'a' && c <= 'z' ) return c-'a';
if( c >= 'A' && c <= 'Z' ) return c-'A'+;
if( c >= '' && c <= '' ) return c-''+;
} char getC( int i )
{
if( i >= && i <= ) return 'a'+i;
if( i >= && i <= ) return 'A'+i-;
if( i >= && i <= ) return ''+i-;
} void pushUp( int index )
{
t[index].size = t[index<<].size + t[index<<|].size;
for ( int i = ; i < ; i++ )
t[index].clu[i] = t[index<<].clu[i] + t[index<<|].clu[i];
} void build( int l, int r, int index )
{
t[index].lhs = l; t[index].rhs = r;
if( l == r )
{ t[index].size = ; t[index].clu[getI(str[l-])] = ; return; }
int mid = (l+r)>>;
build(l, mid, index<<);
build(mid+, r, index<<|);
pushUp(index);
} int find( int index, int x )
{
int l = t[index].lhs, r = t[index].rhs;
if( l == r ) return l;
if( t[index<<].size >= x ) return find( index<<, x );
else return find( index<<|, x-t[index<<].size );
} void change( int index, int l, int r, int x )
{
int L = t[index].lhs, R = t[index].rhs;
if ( r < L || l > R ) return;
if ( !t[index].clu[x] ) return;
if ( L == R ) { t[index].size--; t[index].clu[x]--; return; }
change( index<<, l, r, x );
change( index<<|, l, r, x );
t[index].size = t[index<<].size + t[index<<|].size;
t[index].clu[x] = t[index<<].clu[x] + t[index<<|].clu[x];
} void print( int i )
{
int L=t[i].lhs,R=t[i].rhs;
if ( L==R )
{
for ( int j = ; j < ; j++ )
if ( t[i].clu[j] ) printf("%c",getC(j));
return;
}
print(i<<);
print(i<<|); } int main()
{
int len, q;
cin >> len >> q;
scanf("%s", str);
build(, len, ); int x, y;
char c;
for ( int i = ; i <= q; i++ )
{
scanf( "%d %d %c", &x, &y, &c );
int l = find( , x );
int r = find( , y );
change( , l, r, getI(c) );
}
print();
printf("\n");
return ;
}

Codeforces 899F Letters Removing 线段树/树状数组的更多相关文章

  1. codeforces 899F Letters Removing set+树状数组

    F. Letters Removing time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. 899F - Letters Removing

    Codeforces 899F - Letters Removing 思路:考虑一下怎么找到输入的l和r在原来串中的位置,我们想到用前缀和来找,一开始所有位置都为1,删掉后为0,那么前缀和为l的位置就 ...

  3. Codeforces 889F Letters Removing(二分 + 线段树 || 树状数组)

    Letters Removing 题意:给你一个长度为n的字符串,然后进行m次删除操作,每次删除区间[l,r]内的某个字符,删除后并且将字符串往前补位,求删除完之后的字符串. 题解:先开80个set ...

  4. CodeForces -163E :e-Government (AC自动机+DFS序+树状数组)

    The best programmers of Embezzland compete to develop a part of the project called "e-Governmen ...

  5. Codeforces 899 F. Letters Removing (二分、树状数组)

    题目链接:Letters Removing 题意: 给你一个长度为n的字符串,给出m次操作.每次操作给出一个l,r和一个字符c,要求删除字符串l到r之间所有的c. 题解: 看样例可以看出,这题最大的难 ...

  6. Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Problem E (Codeforces 831E) - 线段树 - 树状数组

    Vasily has a deck of cards consisting of n cards. There is an integer on each of the cards, this int ...

  7. Codeforces Round #225 (Div. 1) C. Propagating tree dfs序+ 树状数组或线段树

    C. Propagating tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/383/p ...

  8. codeforces 540E 离散化技巧+线段树/树状数组求逆序对

    传送门:https://codeforces.com/contest/540/problem/E 题意: 有一段无限长的序列,有n次交换,每次将u位置的元素和v位置的元素交换,问n次交换后这个序列的逆 ...

  9. CodeForces 173E Camping Groups 离线线段树 树状数组

    Camping Groups 题目连接: http://codeforces.com/problemset/problem/173/E Description A club wants to take ...

随机推荐

  1. 牛客网NOIP赛前集训营-普及组(第七场)

    链接:C 来源:牛客网 牛牛的同学给牛牛表演了一个读心术:牛牛先任意选定一个非负整数,然后进行N次操作:每次操作前,假设牛牛当前的数是a,那么这个操作可能是a = a + x, 或者a = a * x ...

  2. USACO 2003 Fall Orange Cow Exhibition /// 负数01背包 oj22829

    题目大意: 输入n 接下来n行 每行输入 a b 输出n行中 a+b总和最大的同时满足 所有a总和>=0所有b总和>=0的值 负数的01背包应该反过来 w[i]为正数时 需要从大往小推 即 ...

  3. Python对象和类

    Python 里的所有数据都是以对象形式存在的,对象是类的实例. 定义类(class) 使用class来定义一个类. 比如,定义一个cat类,如下: class Cat(): def __init__ ...

  4. chown命令使用

    1.原文件为root权限,改为用户所属权限包括文件夹以下的目录这里必须有R chown -R usrname:username /file 2.修改 tmp 目录为可写权限 chmod -R 777 ...

  5. (转)线程池 ExecutorService 详细介绍以及注意点区别

    线程池 ExecutorService 相信java开发都用到,这里做个简单笔记 一 Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池 ...

  6. vue/cli 3.0脚手架搭建

    在vue 2.9.6中,搭建vue-cli脚手架的流程是这样的: 首先 全局安装vue-cli,在cmd中输入命令: npm install --global vue-cli  安装成功:  安装完成 ...

  7. 导入excel并进行数据提取

    /** * @description: 导入excel并进行数据提取 * @param {type} * @return: */ Vue.prototype.$importExcel = functi ...

  8. 关于电容与Q值

    1, 电容模型 电容阻抗可以表示为: 可算得自谐振频率点为: 在该点,容抗与感抗差为0,电容表现出纯电阻性. 2, 阻抗曲线 自谐点是区分电容器呈容性还是感性的分界点.从阻抗曲线看,在自谐点附近阻抗较 ...

  9. 在Xsheel Linux上安装nodejs和npm

    最近window系统转向linux系统开发,linux系统的确适合程序员的开发. 作为前端安装了nodejs和npm,遇到了一些坑,赶紧记录下来 第一种安装方法:安装nodejs  : sudo  a ...

  10. System.Web.Mvc.FilePathResult.cs

    ylbtech-System.Web.Mvc.FilePathResult.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutral, Pub ...