• Codeforces 558E A Simple Task (计数排序&&线段树优化)


    题目链接:http://codeforces.com/contest/558/problem/E
    E. A Simple Task

    time limit per test
    5 seconds

    memory limit per test
    512 megabytes

    input
    standard input

    output
    standard output
     
    This task is very simple. Given a string S of length n and q queries
     each query is on the format i j k which
     means sort the substring consisting of the characters from i to j in
     non-decreasing order if k = 1 or in non-increasing order if k = 0.
    Output the final string after applying the queries.
     
    Input
    The first line will contain two integers n, q (1 ≤ n ≤ 105, 0 ≤ q ≤ 50 000),
     the length of the string and the number of queries respectively.
    Next line contains a string S itself. It contains only lowercase English letters.
    Next q lines will contain three integers each i, j, k (1 ≤ i ≤ j ≤ n, ).
     
    Output
    Output one line, the string S after applying the queries.
     
    Sample test(s)
     
    input
    10 5
    abacdabcda
    7 10 0
    5 8 1
    1 4 0
    3 6 0
    7 10 1
     

    output
    cbcaaaabdd
     
    input
    10 1
    agjucbvdfk
    1 10 1
     

    output
    abcdfgjkuv
     
     
    Note
    First sample test explanation:
    题意:就是说给你一个n长度字符串,进行q次操作,k=1时要对着一个区间的字符进行
    非递减排序,k=2时相反,最后输出操作后的字符串
    思路:正常写法肯定超时,那就可肯定要优化,采取线段树优化查询和更改都是log2(n)
    线短路算法:https://blog.csdn.net/zearot/article/details/48299459
    代码有解析,我就不多说了!
    上代码:
      1 #include<stdio.h>
      2 #include<iostream>
      3 #include<algorithm>
      4 #include<string.h>
      5 using namespace std;
      6 const int maxn=100005;
      7 #define lson(x) x<<1
      8 #define rson(x) x<<1|1
      9 #define L(x) tree[x].l;
     10 #define R(x) tree[x].r;
     11 #define Hav[x] tree[x].hav
     12 #define Lazy(x) tree[x].lazy
     13 char ans[maxn],str[maxn];
     14 int cnt[27];
     15 struct Segtree  //声明26棵树来存每个字母
     16 {
     17     int l,r,hav,m,lazy;
     18 }tree[27][maxn<<2];
     19 void pushup(int root,int id)  //区间求和,因为一个父区间中某个字母的个数是所有子区间字母个数的和
     20 {
     21     tree[id][root].hav=tree[id][root<<1].hav+tree[id][root<<1|1].hav;
     22 }
     23 void pushdown(int root,int id) //下推标记,在这过程中将所个区间全部填满,(为什么填满要看主函数)
     24 {
     25     if(tree[id][root].lazy<0) return;
     26     tree[id][root<<1].hav=(tree[id][root<<1].r-tree[id][root<<1].l)*tree[id][root].lazy;
     27     tree[id][root<<1].lazy=tree[id][root].lazy;
     28     tree[id][root<<1|1].hav=(tree[id][root<<1|1].r-tree[id][root<<1|1].l)*tree[id][root].lazy;
     29     tree[id][root<<1|1].lazy=tree[id][root].lazy;
     30     tree[id][root].lazy=-1;  //父标记删除,子标记续上
     31 }
     32 //void build(int l,int r,int root,int id)
     33 //{ //建一颗总长度为r-l+1的树,其中第一个父节点区间长度最大,处于树的顶端,剩下的依次减少,且相互之间不能重复
     34 //    int mid=(l+r)>>1;
     35 //    tree[id][root].m=mid;  //记录中间节点,中后可能会使用(我后面代码有的时候没有使用)
     36 //    tree[id][root].l=l;
     37 //    tree[id][root].r=r;
     38 //    tree[id][root].lazy=-1;  //标记复原位
     39 //    if(r-l==1)   //按模板来说应该是r==l时才进入,但是主函数中字符数组存值是从0--n-1,
     40 //    {  //但是我建图的时候用的是1--n+1,其实n+1并没有,所以就相当于这个时候已经到树的最底部了
     41 //        //这一点多做一些题就会学到好些骚操作
     42 //        tree[id][root].hav=(str[l-1]=='a'+id);
     43 //        return;
     44 //    }
     45 //    build(l,mid,root<<1,id);
     46 //    build(mid+1,r,root<<1|1,id);
     47 //    pushup(root,id);
     48 //}
     49 void build(int l,int r,int n,int id)
     50 
     51 {
     52 
     53     int m=(l+r)>>1;
     54 
     55     tree[id][n].l=l;
     56 
     57     tree[id][n].r=r;
     58 
     59     tree[id][n].m=m;
     60 
     61     tree[id][n].lazy=-1;
     62     if(r-l==1)
     63     {
     64         tree[id][n].hav=(str[l-1]=='a'+id);
     65         return;
     66     }
     67 
     68     if(r-l==1)
     69 
     70     {
     71 
     72         tree[id][n].hav=(str[l-1]=='a'+id);
     73 
     74         return;
     75 
     76     }
     77 
     78     build(l,m,n<<1,id);
     79     //因为当(n+n+1)>>1的值为n,一旦加一,那就超范围了
     80     //而且本题再给树底部赋值的时候是用str[l-1]来判断的,由此可见,Segtree结构体里面存的范围和实际操作相差一
     81     build(m,r,n<<1|1,id);  //————————我的代码是这里错了,我写的是“m+1”——————————————
     82 
     83     pushup(n,id);
     84 
     85 }
     86 //void update(int l,int r,int root,int ops,int id)
     87 //{ //更新操作,把给出的这一部分区间全部填满(这里说的全部填满就是tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*1(数字);)
     88 //    if(tree[id][root].l==l && tree[id][root].r==r)
     89 //    {
     90 //        tree[id][root].hav=(tree[id][root].r-tree[id][root].l)*ops;
     91 //        tree[id][root].lazy=ops;
     92 //        return;
     93 //    }
     94 //    //先下推标记,要不然有的区间还没更新就去查找就会错
     95 //    pushdown(root,id);
     96 //    int mid=(tree[id][root].l+tree[id][root].r)>>1;
     97 //    if(r<=mid) //满足这一个或下一个条件就说明所求区间就在此父节点的一个子区间内
     98 //    {
     99 //        update(l,r,root<<1,ops,id);
    100 //    }
    101 //    else if(l>=mid)
    102 //    {
    103 //        update(l,r,root<<1|1,ops,id);
    104 //    }
    105 //    else
    106 //    {
    107 //        update(l,mid,root<<1,ops,id);
    108 //        update(mid,r,root<<1|1,ops,id);
    109 //    }
    110 //    pushup(root,id);
    111 //}
    112 void update(int l,int r,int n,int op,int id)
    113 
    114 {
    115 
    116     if(tree[id][n].l==l && tree[id][n].r==r)
    117 
    118     {
    119 
    120         tree[id][n].hav=(tree[id][n].r-tree[id][n].l)*op;
    121 
    122         tree[id][n].lazy=op;
    123 
    124         return;
    125 
    126     }
    127 
    128     pushdown(n,id);
    129 
    130     if(r<=tree[id][n].m)update(l,r,n<<1,op,id);
    131 
    132     else if(l>=tree[id][n].m)update(l,r,n<<1|1,op,id);
    133 
    134     else
    135 
    136     {
    137 
    138         update(l,tree[id][n].m,n<<1,op,id);
    139 
    140         update(tree[id][n].m,r,n<<1|1,op,id);
    141 
    142     }
    143 
    144     pushup(n,id);
    145 
    146 }
    147 int query(int l,int r,int n,int id)
    148 
    149 {
    150 
    151     if(tree[id][n].l==l && tree[id][n].r==r)
    152 
    153         return tree[id][n].hav;
    154 
    155     pushdown(n,id);
    156 
    157     if(r<=tree[id][n].m)
    158 
    159         return query(l,r,n<<1,id);
    160 
    161     if(l>=tree[id][n].m)
    162 
    163         return query(l,r,n<<1|1,id);
    164 
    165     return query(l,tree[id][n].m,n<<1,id)
    166 
    167             +query(tree[id][n].m,r,n<<1|1,id); //————————这里的tree[id][n].m也不能多加一————————————————
    168 
    169 }
    170 //int query(int l,int r,int root,int id)
    171 //{  //查找和更新大多都一样
    172 //    if(tree[id][root].l==l && tree[id][root].r==r)
    173 //    {
    174 //        return tree[id][root].hav;
    175 //    }
    176 //    pushdown(root,id);
    177 //    int mid=(tree[id][root].l+tree[id][root].r)>>1;
    178 //    if(r<=mid) return query(l,r,root<<1,id);
    179 //    else if(l>=mid) return query(l,r,root<<1|1,id);
    180 //    return query(l,mid,root<<1,id)+query(mid+1,r,root<<1|1,id);
    181 //}
    182 //int main()
    183 //{
    184 //    int n,q;
    185 //    scanf("%d%d",&n,&q);
    186 //    scanf("%s",str);
    187 //    for(int i=0;i<26;++i) build(1,n+1,1,i);
    188 //    int l,r,k;
    189 //    while(q--)
    190 //    {
    191 //        scanf("%d%d%d",&l,&r,&k);
    192 //        for(int i=0;i<26;++i)
    193 //        {
    194 //            cnt[i]=query(l,r+1,1,i);
    195 //            update(l,r+1,1,0,i);
    196 //        }
    197 //        if(k==1)
    198 //        {
    199 //            int loc=l;
    200 //            for(int i=0;i<26;++i)
    201 //            {
    202 //                if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
    203 //                loc+=cnt[i];
    204 //            }
    205 //        }
    206 //        else
    207 //        {
    208 //            int loc=l;
    209 //            for(int i=25;i>=0;--i)
    210 //            {
    211 //                if(cnt[i]) update(loc,loc+cnt[i],1,1,i);
    212 //                loc+=cnt[i];
    213 //            }
    214 //        }
    215 //    }
    216 //    for(int i=1;i<=n;++i)
    217 //    {
    218 //        for(int j=0;j<26;++j)
    219 //        {
    220 //            if(query(i,i+1,1,j))
    221 //            {
    222 //                ans[i-1]='a'+j;
    223 //                break;
    224 //            }
    225 //        }
    226 //    }
    227 //    printf("%s
    ",ans);
    228 //    return 0;
    229 //}
    230 int main()
    231 
    232 {
    233 
    234     int n,q;
    235 
    236     scanf("%d%d",&n,&q);
    237 
    238     scanf("%s",str);
    239 
    240     for(int i=0;i<26;i++)build(1,n+1,1,i);
    241     //先按照原来的字符在数组里面的顺序对线段树进行填充
    242     int l,r,k;
    243 
    244     while(q--)
    245 
    246     {
    247 
    248         scanf("%d%d%d",&l,&r,&k);
    249 
    250         for(int i=0;i<26;i++)
    251 
    252         {
    253 
    254             cnt[i]=query(l,r+1,1,i);
    255             //找出来这个被要求的区间内有多少这个字母
    256             update(l,r+1,1,0,i);
    257             //再把这个区间全部初始化为0
    258         }
    259         //判断一下是让顺序还是逆序,如果是按顺序排,那就让填充的时候字母正着填充
    260         if(k==1)
    261 
    262         {
    263 
    264             int loc=l;
    265 
    266             for(int i=0;i<26;i++)
    267 
    268             {
    269 
    270                 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i);
    271              
    272                 loc+=cnt[i];
    273 
    274             }
    275 
    276         }
    277 
    278         else
    279 
    280         {
    281 
    282             int loc=l;
    283 
    284             for(int i=25;i>=0;i--)
    285 
    286             {
    287 
    288                 if(cnt[i]>0)update(loc,loc+cnt[i],1,1,i);
    289 
    290                 loc+=cnt[i];
    291 
    292             }
    293 
    294         }
    295 
    296     }
    297     //这个就相当于一个位置一个位置的查询
    298     for(int i=1;i<=n;i++)
    299 
    300         for(int j=0;j<26;j++)
    301 
    302             if(query(i,i+1,1,j))
    303 
    304             {
    305 
    306                 ans[i-1]='a'+j;
    307 
    308                 break;
    309 
    310             }
    311 
    312     printf("%s
    ",ans);
    313 
    314     return 0;
    315 
    316 }
    View Code
     
  • 相关阅读:
    组策略导入导出secedit
    ipad常见错误
    ipad系统路径
    内核操作注册表例子
    WoW64子系统
    win2003 shutdown命令
    regedit 导入注册表
    windbg for CLR
    WM_POWERBROADCAST
    OpenSSL命令
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/10776783.html
Copyright © 2020-2023  润新知