zoukankan      html  css  js  c++  java
  • CF558E A Simple Task

    题目大意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过5000个操作。

    每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序。

    最后输出最终的字符串

    首先这么想想,对于一段区间的排序,排完序的样子和排序之前每个字母的位置并没有关系,而是和每一个字母出现的次数有关。所以我们对于每一次操作,统计出区间中每一个字母出现了多少次,然后按字典序排序就行。更确切的说,就是这个区间中的哪一个部分都改成某一个字母,区间修改。

    既然是区间修改,那么就可以用线段树实现。不过这样的话,打lazy标记就显得不是很方便。为此,我们可以开26个线段树,每一个字母开一个长度为n的权值线段树,如果第i为是这个字母,我们就把这一位改成1,然后统计区间中这个字母有多少个,就相当于求区间和了。至于修改,那就是将这个字母所在线段树的区间全都改成1.然后把操作区间的别的地方改成0即可。

    举个栗子:

    acbcaab

    然后将[1, 6]按升序排序。

    那么我们首先分别在a, b, c所在的线段树上查到了[1, 6]的区间和,即统计出了每个字母的出现次数。

    然后排序的时候,对于a所在的线段树,我们将[1, 3]都改成了1,[4, 6]改成了0;对于b所在线段树,我们将[4, 4]改成了1,[1, 3]和[5, 6]改成了0;对于c,我们将[5, 6]改成了1,[1, 4]改成了0.

    这样这个区间就排完序了。

    那么怎么输出最终答案呢?

    只要对于每一位,暴力的从0到26循环,看哪个字母在这一位上是1,就说明这一位是这个字母了。

    配合break,时间复杂度最坏为O(nlogn * 26)

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<cstdlib>
      7 #include<cctype>
      8 #include<vector>
      9 #include<stack>
     10 #include<queue>
     11 using namespace std;
     12 #define enter printf("
    ")
     13 #define space printf(" ")
     14 #define Mem(a) memset(a, 0, sizeof(a))
     15 typedef long long ll;
     16 typedef double db;
     17 const int INF = 0x3f3f3f3f;
     18 const db eps = 1e-8;
     19 const int maxn = 2e7 + 5;
     20 inline int read()
     21 {
     22     int ans = 0;
     23     char ch = getchar(), last = ' ';
     24     while(!isdigit(ch)) {last = ch; ch = getchar();}
     25     while(isdigit(ch))
     26     {
     27         ans = ans * 10 + ch - '0'; ch = getchar();    
     28     }
     29     if(last == '-') ans = -ans;
     30     return ans;
     31 }
     32 inline void write(ll x)
     33 {
     34     if(x < 0) x = -x, putchar('-');
     35     if(x >= 10) write(x / 10);
     36     putchar('0' + x % 10);
     37 }
     38 
     39 int n, q;
     40 char s[100005];
     41 
     42 int cnt = 0, root[30], lson[maxn], rson[maxn], l[maxn], r[maxn];    
     43 //lson[now]和rson[now]分别记录now的左右儿子的编号,代替了now << 1和 now <<1 | 1 
     44 int sum[maxn], lazy[maxn];
     45 void build(int& now, int L, int R)    //我这个写法是先吧所有点开好了,不是动态开点(竟然比某位大佬的动态开点快) 
     46 {
     47     now = ++cnt; lazy[now] = -1;
     48     l[now] = L; r[now] = R;
     49     if(L == R) return;
     50     int mid = (L + R) >> 1;
     51     build(lson[now], L, mid);
     52     build(rson[now], mid + 1, R);
     53 }
     54 void add(int now, int id)
     55 {
     56     if(l[now] == r[now]) {sum[now]++; return;}
     57     int mid = (l[now] + r[now]) >> 1;
     58     if(id <= mid) add(lson[now], id);
     59     else add(rson[now], id);
     60     sum[now] = sum[lson[now]] + sum[rson[now]];
     61 }
     62 void pushdown(int now)
     63 {
     64     if(lazy[now] != -1)        //因为lazy[now]=0代表将区间都改为0,所以没有标记要换一个记号 
     65     {
     66         sum[lson[now]] = (r[lson[now]] - l[lson[now]] + 1) * lazy[now];
     67         sum[rson[now]] = (r[rson[now]] - l[rson[now]] + 1) * lazy[now];
     68         lazy[lson[now]] = lazy[now];
     69         lazy[rson[now]] = lazy[now];
     70         lazy[now] = -1;        
     71     }
     72 
     73 }
     74 void update(int now, int L, int R, int d)
     75 {
     76     if(L == l[now] && R == r[now])
     77     {
     78         sum[now] = (r[now] - l[now] + 1) * d; 
     79         lazy[now] = d; return;
     80     }
     81     pushdown(now);
     82     int mid = (l[now] + r[now]) >> 1;
     83     if(R <= mid) update(lson[now], L, R, d);
     84     else if(L > mid) update(rson[now], L, R, d);
     85     else {update(lson[now], L, mid, d); update(rson[now], mid + 1, R, d);}
     86     sum[now] = sum[lson[now]] + sum[rson[now]];
     87 }
     88 int query(int now, int L, int R)
     89 {
     90     if(!sum[now]) return 0;        //优化     
     91     if(L == l[now] && R == r[now]) return sum[now];
     92     pushdown(now);
     93     int mid = (l[now] + r[now]) >> 1;
     94     if(R <= mid) return query(lson[now], L, R);
     95     else if(L > mid) return query(rson[now], L, R);
     96     else return query(lson[now], L, mid) + query(rson[now], mid + 1, R);
     97 }
     98 
     99 int main()
    100 {
    101     n = read(); q = read();
    102     scanf("%s", s + 1);
    103     for(int i = 0; i < 26; ++i) build(root[i], 1, n);
    104     for(int i = 1; i <= n; ++i)    add(root[s[i] - 'a'], i);
    105     for(int i = 1; i <= q; ++i)
    106     {
    107         int L = read(), R = read(), k = read();
    108         if(k)
    109         {
    110             int pre = L - 1;
    111             for(int j = 0; j < 26; ++j)     //枚举每一棵线段树 
    112             {
    113                 int ssum = query(root[j], L, R);
    114                 if(ssum) 
    115                 {
    116                     update(root[j],L,R,0);        //先都改成0,在局部覆盖1 
    117                     update(root[j], pre + 1, pre + ssum, 1);
    118                 }
    119                 pre += ssum;
    120             }
    121         }
    122         else
    123         {
    124             int pre = L - 1;
    125             for(int j = 25; j >= 0; --j)     //降序,就倒着枚举 
    126             {
    127                 int ssum = query(root[j], L, R);
    128                 if(ssum) 
    129                 {
    130                     update(root[j],L,R,0);
    131                     update(root[j], pre + 1, pre + ssum, 1);
    132                 }
    133                 pre += ssum;
    134             }
    135         }
    136     }
    137     for(int i = 1; i <= n; ++i)        //很暴力的查询 
    138         for(int j = 0; j < 26; ++j)
    139             if(query(root[j], i, i)) {printf("%c", 'a' + j); break;}
    140     enter;
    141     return 0;
    142 }

    这道题时限5秒,然而还特别容易TLE,所以得做好常数优化工作。

    据说某位大佬线段树上每个节点记录26个字母出现的情况,所以只开了一棵线段树,自然就十分的快了,毫无TLE的烦恼。(很显然,我不会写,要不就不讲上述的方法了……)

  • 相关阅读:
    Effective Java 19 Use interfaces only to define types
    Effective Java 18 Prefer interfaces to abstract classes
    Effective Java 17 Design and document for inheritance or else prohibit it
    Effective Java 16 Favor composition over inheritance
    Effective Java 15 Minimize mutability
    Effective Java 14 In public classes, use accessor methods, not public fields
    Effective Java 13 Minimize the accessibility of classes and members
    Effective Java 12 Consider implementing Comparable
    sencha touch SortableList 的使用
    sencha touch dataview 中添加 button 等复杂布局并添加监听事件
  • 原文地址:https://www.cnblogs.com/mrclr/p/9409858.html
Copyright © 2011-2022 走看看