http://codeforces.com/problemset/problem/159/C
题意:
给你一个字符串s,给出一个数k,k倍的s串组成新串str。然后给出n个操作,每个操作对应着pi,ci意思是将第pi个字符ci从str中删除,求最后得到的字符串。
思路:
首先我想到的是利用vector里面的erase快速的删除,开一个vc[26]来存取每个字符然后模拟删除过程,才开始自己一维erase的时间复杂度为O(1)如果这样肯定不会超时,结果事与愿违果真TLE了。问了问别人晚会上搜了搜原来erase函数的平均复杂度竟然是O(n)这样固然超时,可是看了看AC的代码里面也有用vector + erase写的,只是标记了以下过的。其实这不是正解。
正解在此,看了以下官方的解体报告,开一个26个线段树,于是就往线段树方向思考。果然,我们只要开一个二位的线段树val[27][4*N]然后没出现相应的字符我们就将其插入,线段树记录区间满足条件的个数。然后每次删除时我们只要利用线段树的查询第整个区间里面的第几个来删除就好了。这里在执行删除与更新时弄混了,导致在第三组数据上错了好多次,吐槽以下给的大数据都带省略号的怎么改啊,悲粹啊..
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 200007 using namespace std; //freopen("data.in","r",stdin); int pos[27][N]; int val[27][N*4]; char str[107]; int num[27]; int lp; void pushup(int rt,int mk){ val[mk][rt] = val[mk][rt<<1] + val[mk][rt<<1|1]; } void update(int mk,int d,int sc,int l,int r,int rt){ if (l == r){ val[mk][rt] = sc; return ; } int m = (l + r)>>1; if (d <= m) update(mk,d,sc,lc); else update(mk,d,sc,rc); pushup(rt,mk); } //注意modify与update的区别,才开始搞混了。 void modify(int mk,int d,int sc,int l,int r,int rt){ if (l == r){ val[mk][rt] = sc; return ; } int m = (l + r)>>1; if (d <= val[mk][rt<<1]) modify(mk,d,sc,lc); else{ d -= val[mk][rt<<1]; modify(mk,d,sc,rc); } pushup(rt,mk); } int query(int mk,int d,int l,int r,int rt){ if (l == r){ lp = l;//记录删除的位置 return pos[mk][l];//返回其坐标, } int m = (l + r)>>1; if (d <= val[mk][rt<<1]) return query(mk,d,lc); else{ d -= val[mk][rt<<1]; return query(mk,d,rc); } } int main(){ //freopen("data.in","r",stdin); int n,i,j; int k; n = 200001; scanf("%d%s",&k,str); int len = strlen(str); int p = 0; int no = 0; CL(val,0); CL(num,0); for (i = 0; i < k; ++i){ for (j = 0; j < len; ++j){ int mk = str[j] - 'a'; pos[mk][++num[mk]] = p++;//记录每个点的下标,方便用于按下标从小到大排序 update(mk,num[mk],1,1,n,1); no++; } } char tp[3]; int q,d; scanf("%d",&q); while (q--){ scanf("%d%s",&d,tp); int mk = tp[0] - 'a'; modify(mk,d,0,1,n,1); no--; } int pp; int Lp; while (no--){ int MIN = inf; for (i = 0; i < 26; ++i){ if (val[i][1] != 0) { int tmp = query(i,1,1,n,1); if (MIN > tmp){ MIN = tmp; pp = i; Lp = lp; } } } update(pp,Lp,0,1,n,1); printf("%c",pp + 'a'); } printf("\n"); return 0; }
第二种正解就是树状数组+二分了。
其实思路差不多,树状数组果然比线段树代码量少的多啊。我们把整个str分为26个应为字符对应的位置,然后每次删除时,二分找到第d个然后将其删除,二分中求和利用树状数组。然后hash表示每个位置是否被删除了,然后输出即可。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 200007 using namespace std; //freopen("data.in","r",stdin); int len; bool hash[N]; struct BIT{ #define lowbit(x) ((x) & (-x)); int tree[N]; void init(){ CL(tree,0); } inline void update(int x,int val){ while (x <= len){ tree[x] += val; x += lowbit(x); } } inline int query(int x){ int sum = 0; while (x > 0){ sum += tree[x]; x -= lowbit(x); } return sum; } }a[27]; char str[105]; int main(){ // freopen("data.in","r",stdin); int i,j; int k; scanf("%d%s",&k,str); int L = strlen(str); len = L*k; int pos = 0; for (i = 0 ; i < 26; ++i) a[i].init(); for (i = 0; i < k; ++i){ for (j = 0; j < L; ++j){ a[str[j] - 'a'].update(++pos,1); } } CL(hash,0); int q,d; char ask[3]; scanf("%d",&q); while (q--){ scanf("%d%s",&d,ask); int mk = ask[0] - 'a'; int ans = 0; int l = 1, r = len; while (l <= r){ int m = (l + r)>>1; int tmp = a[mk].query(m); if (tmp >= d){ r = m - 1;// 注意这里要r = m - 1 是因为1 到 m里面不是每一个都满足条件的只是和等于d ans = m; } else if (tmp < d){ l = m + 1; } } hash[ans] = true; a[mk].update(ans,-1); } pos = 0; for (i = 0 ; i < k; ++i){ for (j = 0; j < L; ++j){ if (!hash[++pos]) printf("%c",str[j]); } } printf("\n"); return 0; }