虽然每次给一个区间,但是可以看作在区间内进行数个点操作,同样数列下标是动态变化的,如果我们将每个字符出现看作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. 如何在neo4j中创建新数据库?

    解决方案一: 由于使用Neo3.x创建新数据库而不删除现有数据库,所以只需在$NEO4J_HOME的conf的目录编辑neo4j.conf. 搜寻dbms.active_database=,其默认值应 ...

  2. 面试系列16 dubbo负载均衡策略和集群容错策略都有哪些?动态代理策略呢

    (1)dubbo负载均衡策略 1)random loadbalance 默认情况下,dubbo是random load balance随机调用实现负载均衡,可以对provider不同实例设置不同的权重 ...

  3. 确认(confirm 消息对话框)语法:confirm(str); 消息对话框通常用于允许用户做选择的动作,如:“你对吗?”等。弹出对话框(包括一个确定按钮和一个取消按钮)

    确认(confirm 消息对话框) confirm 消息对话框通常用于允许用户做选择的动作,如:"你对吗?"等.弹出对话框(包括一个确定按钮和一个取消按钮). 语法: confir ...

  4. string json list

    String str="[{\"cIndex\":14,\"column\":\"nextAdvice\",\"id\& ...

  5. 位运算 - 左移右移运算符 >>, <<, >>>

    1-左移运算符m<<n,表示把m左移n位.左移n位的时候,最左边的n位数将被丢弃,同时在最右边补上n个0.例如: 00001010<<2 = 00101000 10001010 ...

  6. 【转载】linux进程及进程控制

    Linux进程控制   程序是一组可执行的静态指令集,而进程(process)是一个执行中的程序实例.利用分时技术,在Linux操作系统上同时可以运行多个进程.分时技术的基本原理是把CPU的运行时间划 ...

  7. malloc在函数内分配内存问题

    malloc函数用法可参考:C语言中 malloc函数用法 及 malloc函数 代码: void fun(char * p) { p=(); } void main() { char *p; fun ...

  8. Java - 关于覆盖和重写的总结

    公众号偶然看到的一个帖子,构造方法,类方法,final方法,哪些能覆盖,哪些能重载,初学时也是被这些术语搞的很迷糊 现在有时间了对这些做一个总结.全是自己的语言,可能不是很全面,表达意思应该够清楚 一 ...

  9. WPF+MVVM+EF示例1

    实现了那些功能,先看看效果图: 项目工程目录: 接下来开始具体的步骤: 第一步:在VS中新建工程 第二步:使用NuGet 安装EntityFramework 第三步:使用NuGet 安装EntityF ...

  10. 08-5-switch

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...