• 模拟11 题解


    A. string

    类似 HEOI2016排序

    排序这道题因为只询问单点最终答案,二分答案,

    将小于和大于等于答案的数分别设为0 1,

    用线段树维护0 1的排序即可。

     

    算法一:

    本题中的1~n变成了0~25(即a~z),单点询问变成了全体询问。

    仿照排序那道题的做法,线段树优化桶排序。

    维护每个节点每个字符的cnt值,区间查询后区间赋值即可。

    然而up和down函数都是$O(26)$的,加上修改操作最多被拆分为26个,

    总的复杂度为$O(nlogn26^2)$。

    需要卡常。

    考场上码出了80分,一次优化成了70,再一次优化成了60。

    考后把第一次拿出来,加了快读,卡到了90。

    循环展开AC。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #define lch p<<1
      5 #define rch p<<1|1
      6 using namespace std;
      7 const int N=100010;
      8 int n,m,num[28];
      9 struct node{
     10     int l,r,cnt[26],set;
     11 }s[N<<2];
     12 char ch;
     13 inline void up(int p){
     14     s[p].cnt[0]=s[lch].cnt[0]+s[rch].cnt[0];
     15     s[p].cnt[1]=s[lch].cnt[1]+s[rch].cnt[1];
     16     s[p].cnt[2]=s[lch].cnt[2]+s[rch].cnt[2];
     17     s[p].cnt[3]=s[lch].cnt[3]+s[rch].cnt[3];
     18     s[p].cnt[4]=s[lch].cnt[4]+s[rch].cnt[4];
     19     s[p].cnt[5]=s[lch].cnt[5]+s[rch].cnt[5];
     20     s[p].cnt[6]=s[lch].cnt[6]+s[rch].cnt[6];
     21     s[p].cnt[7]=s[lch].cnt[7]+s[rch].cnt[7];
     22     s[p].cnt[8]=s[lch].cnt[8]+s[rch].cnt[8];
     23     s[p].cnt[9]=s[lch].cnt[9]+s[rch].cnt[9];
     24     s[p].cnt[10]=s[lch].cnt[10]+s[rch].cnt[10];
     25     s[p].cnt[11]=s[lch].cnt[11]+s[rch].cnt[11];
     26     s[p].cnt[12]=s[lch].cnt[12]+s[rch].cnt[12];
     27     s[p].cnt[13]=s[lch].cnt[13]+s[rch].cnt[13];
     28     s[p].cnt[14]=s[lch].cnt[14]+s[rch].cnt[14];
     29     s[p].cnt[15]=s[lch].cnt[15]+s[rch].cnt[15];
     30     s[p].cnt[16]=s[lch].cnt[16]+s[rch].cnt[16];
     31     s[p].cnt[17]=s[lch].cnt[17]+s[rch].cnt[17];
     32     s[p].cnt[18]=s[lch].cnt[18]+s[rch].cnt[18];
     33     s[p].cnt[19]=s[lch].cnt[19]+s[rch].cnt[19];
     34     s[p].cnt[20]=s[lch].cnt[20]+s[rch].cnt[20];
     35     s[p].cnt[21]=s[lch].cnt[21]+s[rch].cnt[21];
     36     s[p].cnt[22]=s[lch].cnt[22]+s[rch].cnt[22];
     37     s[p].cnt[23]=s[lch].cnt[23]+s[rch].cnt[23];
     38     s[p].cnt[24]=s[lch].cnt[24]+s[rch].cnt[24];
     39     s[p].cnt[25]=s[lch].cnt[25]+s[rch].cnt[25];
     40 }
     41 inline void down(int p){
     42     s[lch].set=s[rch].set=s[p].set;
     43     s[lch].cnt[0]=s[lch].cnt[1]=s[lch].cnt[2]=s[lch].cnt[3]=s[lch].cnt[4]=s[lch].cnt[5]=s[lch].cnt[6]=s[lch].cnt[7]=s[lch].cnt[8]=s[lch].cnt[9]=s[lch].cnt[10]=s[lch].cnt[11]=s[lch].cnt[12]=s[lch].cnt[13]=s[lch].cnt[14]=s[lch].cnt[15]=s[lch].cnt[16]=s[lch].cnt[17]=s[lch].cnt[18]=s[lch].cnt[19]=s[lch].cnt[20]=s[lch].cnt[21]=s[lch].cnt[22]=s[lch].cnt[23]=s[lch].cnt[24]=s[lch].cnt[25]=0;
     44     s[rch].cnt[0]=s[rch].cnt[1]=s[rch].cnt[2]=s[rch].cnt[3]=s[rch].cnt[4]=s[rch].cnt[5]=s[rch].cnt[6]=s[rch].cnt[7]=s[rch].cnt[8]=s[rch].cnt[9]=s[rch].cnt[10]=s[rch].cnt[11]=s[rch].cnt[12]=s[rch].cnt[13]=s[rch].cnt[14]=s[rch].cnt[15]=s[rch].cnt[16]=s[rch].cnt[17]=s[rch].cnt[18]=s[rch].cnt[19]=s[rch].cnt[20]=s[rch].cnt[21]=s[rch].cnt[22]=s[rch].cnt[23]=s[rch].cnt[24]=s[rch].cnt[25]=0;
     45     s[lch].cnt[s[p].set]=s[lch].r-s[lch].l+1;
     46     s[rch].cnt[s[p].set]=s[rch].r-s[rch].l+1;
     47     s[p].set=-1;
     48 }
     49 void build(int p,int l,int r){
     50     s[p].l=l; s[p].r=r; s[p].set=-1;
     51     if(l==r){
     52         scanf(" %c",&ch);
     53         ++s[p].cnt[s[p].set=ch-'a'];
     54         return ;
     55     }
     56     int mid=l+r>>1;
     57     build(lch,l,mid);
     58     build(rch,mid+1,r);
     59     up(p);
     60 }
     61 void query(int p,int l,int r){
     62     if(s[p].l>=l&&s[p].r<=r){
     63         num[0]+=s[p].cnt[0];
     64         num[1]+=s[p].cnt[1];
     65         num[2]+=s[p].cnt[2];
     66         num[3]+=s[p].cnt[3];
     67         num[4]+=s[p].cnt[4];
     68         num[5]+=s[p].cnt[5];
     69         num[6]+=s[p].cnt[6];
     70         num[7]+=s[p].cnt[7];
     71         num[8]+=s[p].cnt[8];
     72         num[9]+=s[p].cnt[9];
     73         num[10]+=s[p].cnt[10];
     74         num[11]+=s[p].cnt[11];
     75         num[12]+=s[p].cnt[12];
     76         num[13]+=s[p].cnt[13];
     77         num[14]+=s[p].cnt[14];
     78         num[15]+=s[p].cnt[15];
     79         num[16]+=s[p].cnt[16];
     80         num[17]+=s[p].cnt[17];
     81         num[18]+=s[p].cnt[18];
     82         num[19]+=s[p].cnt[19];
     83         num[20]+=s[p].cnt[20];
     84         num[21]+=s[p].cnt[21];
     85         num[22]+=s[p].cnt[22];
     86         num[23]+=s[p].cnt[23];
     87         num[24]+=s[p].cnt[24];
     88         num[25]+=s[p].cnt[25];
     89         return ;
     90     }
     91     if(~s[p].set) down(p);
     92     if(l<=s[lch].r) query(lch,l,r);
     93     if(r>=s[rch].l) query(rch,l,r);
     94     up(p);
     95 }
     96 void set(int p,int l,int r,int val){
     97     if(s[p].l>=l&&s[p].r<=r){
     98         s[p].cnt[0]=s[p].cnt[1]=s[p].cnt[2]=s[p].cnt[3]=s[p].cnt[4]=s[p].cnt[5]=s[p].cnt[6]=s[p].cnt[7]=s[p].cnt[8]=s[p].cnt[9]=s[p].cnt[10]=s[p].cnt[11]=s[p].cnt[12]=s[p].cnt[13]=s[p].cnt[14]=s[p].cnt[15]=s[p].cnt[16]=s[p].cnt[17]=s[p].cnt[18]=s[p].cnt[19]=s[p].cnt[20]=s[p].cnt[21]=s[p].cnt[22]=s[p].cnt[23]=s[p].cnt[24]=s[p].cnt[25]=0;
     99         s[p].cnt[val]=s[p].r-s[p].l+1;
    100         s[p].set=val;
    101         return ;
    102     }
    103     if(~s[p].set) down(p);
    104     if(l<=s[lch].r) set(lch,l,r,val);
    105     if(r>=s[rch].l) set(rch,l,r,val);
    106     up(p);
    107 }
    108 void dfs(int p){
    109     if(s[p].l==s[p].r){
    110         putchar('a'+s[p].set);
    111         return ;
    112     }
    113     if(~s[p].set) down(p);
    114     dfs(lch); dfs(rch);
    115 }
    116 inline int read(){
    117     int x=0; char ch=getchar();
    118     while(!isdigit(ch)) ch=getchar();
    119     while(isdigit(ch)){
    120         x=(x<<1)+(x<<3)+(ch^48);
    121         ch=getchar();
    122     }
    123     return x;
    124 }
    125 int main(){
    126     n=read(); m=read();
    127     build(1,1,n);
    128     for(int i=1,l,r,opt;i<=m;++i){
    129         l=read(); r=read(); opt=read();
    130         memset(num,0,sizeof(num));
    131         query(1,l,r);
    132         if(opt){
    133             for(int i=1;i<26;++i) num[i]+=num[i-1];
    134             if(num[0]) set(1,l,l+num[0]-1,0);
    135             for(int i=1;i<26;++i) if(num[i]-num[i-1]) set(1,l+num[i-1],l+num[i]-1,i);
    136         }
    137         else{
    138             for(int i=25;~i;--i) num[i]+=num[i+1];
    139             for(int i=25;~i;--i) if(num[i]-num[i+1]) set(1,l+num[i+1],l+num[i]-1,i);
    140         }
    141     }
    142     dfs(1); puts("");
    143     return 0;
    144 }
    AC

     

    算法二(来自DeepinC):

    思路同样来自HEOI2016排序。

    枚举i,范围为0~25,把大于i的字符设为1,反之设为0。

    对于每个枚举,线段树维护桶排序,执行m次操作。

    每两次枚举中,最终01串中由1变为0的,就是字符i。

    复杂度$O(nlogn26)$然而好像常数巨大,只能卡到70分左右。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define lch p<<1
     5 #define rch p<<1|1
     6 using namespace std;
     7 const int N=100010;
     8 int n,m;
     9 struct node{
    10     int l,r,cnt,set;
    11 }s[N<<2];
    12 char ch[N];
    13 char ans[N];
    14 inline void up(int p){
    15     s[p].cnt=s[lch].cnt+s[rch].cnt;
    16 }
    17 inline void down(int p){
    18     s[lch].set=s[rch].set=s[p].set;
    19     if(s[p].set) s[lch].cnt=s[lch].r-s[lch].l+1,s[rch].cnt=s[rch].r-s[rch].l+1;
    20     else s[lch].cnt=s[rch].cnt=0;
    21     s[p].set=-1;
    22 }
    23 void build(int p,int l,int r,int val){
    24     s[p].l=l; s[p].r=r; s[p].set=-1;
    25     if(l==r){
    26         s[p].cnt=(ch[l]-'a'>val);
    27         return ;
    28     }
    29     int mid=l+r>>1;
    30     build(lch,l,mid,val);
    31     build(rch,mid+1,r,val);
    32     up(p);
    33 }
    34 int query(int p,int l,int r){
    35     if(s[p].l>=l&&s[p].r<=r) return s[p].cnt;
    36     if(~s[p].set) down(p);
    37     int ans=0;
    38     if(l<=s[lch].r) ans+=query(lch,l,r);
    39     if(r>=s[rch].l) ans+=query(rch,l,r);
    40     return ans;
    41 }
    42 void set(int p,int l,int r,int val){
    43     if(s[p].l>=l&&s[p].r<=r){
    44         if(val) s[p].cnt=s[p].r-s[p].l+1;
    45         else s[p].cnt=0;
    46         s[p].set=val;
    47         return ;
    48     }
    49     if(~s[p].set) down(p);
    50     if(l<=s[lch].r) set(lch,l,r,val);
    51     if(r>=s[rch].l) set(rch,l,r,val);
    52     up(p);
    53 }
    54 void dfs(int p,int val){
    55     if(s[p].l==s[p].r){
    56         if(!s[p].cnt&&!ans[s[p].l]) ans[s[p].l]='a'+val;
    57         return ;
    58     }
    59     if(~s[p].set) down(p);
    60     dfs(lch,val);
    61     dfs(rch,val);
    62 }
    63 inline int read(){
    64     int x=0; char ch=getchar();
    65     while(!isdigit(ch)) ch=getchar();
    66     while(isdigit(ch)){
    67         x=(x<<1)+(x<<3)+(ch^48);
    68         ch=getchar();
    69     }
    70     return x;
    71 }
    72 int l[N],r[N];
    73 bool opt[N];
    74 int main(){
    75     //freopen("x.in","r",stdin);
    76     //freopen("me.out","w",stdout);
    77     n=read(); m=read();
    78     scanf("%s",ch+1);
    79     for(int i=1;i<=m;++i) l[i]=read(),r[i]=read(),opt[i]=read();
    80     for(int i=0;i<26;++i){
    81         build(1,1,n,i);
    82         for(int j=1;j<=m;++j){
    83             int cnt=query(1,l[j],r[j]);
    84             if(opt[j]){
    85                 cnt=r[j]-l[j]+1-cnt;
    86                 if(cnt) set(1,l[j],l[j]+cnt-1,0);
    87                 if(l[j]+cnt<=r[j]) set(1,l[j]+cnt,r[j],1);
    88             }
    89             else{
    90                 if(cnt) set(1,l[j],l[j]+cnt-1,1);
    91                 if(l[j]+cnt<=r[j]) set(1,l[j]+cnt,r[j],0);
    92             }
    93         }
    94         dfs(1,i);
    95     }
    96     printf("%s
    ",ans+1);
    97     return 0;
    98 }
    TLE50

     

    算法三(好像是正解):

    用26棵线段树。

    每棵线段树只维护0 1, 分别表示等于或不等于该字符。

    其他同算法一,

    总复杂度也为$O(nlogn26)$。

     

    算法四(来自sdfzdky):

    维护线段树的每个节点是否全部相等,

    如果是则直接返回区间大小,否则继续递归。

    排序过程同算法一。

    根据均摊的思想,复杂度是对的。

    最差情况下$O(nlogn26)$,然而在随机数据下很优秀。

     

    算法五(来自cbx):

    将连续的同一字符区间看作同一个元素。

    暴力桶排序。

    复杂度似乎也是对的。

    B. matrix

    考试时打了状压。

    正解:

    (右侧区间为由右端点m开始的区间,即$[r[i],m]$,左侧区间同理)

    定义$f[i][j]$表示考虑前i列,前i列中有j列在右侧区间放1,已经考虑结束端点小于等于i的左侧区间

    状态定义很奇怪,但是是可以转移的。

    定义$lp[i]$表示前缀和,$lp[i]$的含义是有多少个左侧区间已经在i点结束。

    同理$rp[i]$的含义是i点有多少个右侧区间能放1。

    $f[i][j]=f[i-1][j]+f[i-1][j-1]*(r[i]-j+1)$

    加号前面的内容表示这个点不在右侧区间放1,

    后面的内容表示由可以放1的任何一个右侧区间在这里放1。

    转移后,考虑左侧区间。

    $f[i][j]*=A_{i-j-l[i-1]}^{l[i]-l[i-1]}$。

    $f[m][n]$就是答案。

     

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 using namespace std;
     5 const int N=3010;
     6 const int mod=998244353;
     7 int n,m,dp[N][N],l[N],r[N],js[N],jsinv[N];
     8 inline int qpow(int base,int k,int ans=1){
     9     while(k){
    10         if(k&1) ans=1ll*ans*base%mod;
    11         base=1ll*base*base%mod;
    12         k>>=1;
    13     }
    14     return ans;
    15 }
    16 inline int read(){
    17     int x; scanf("%d",&x); return x;
    18 }
    19 inline int A(int n,int m){
    20     if(m>n) return 0;
    21     return 1ll*js[n]*jsinv[n-m]%mod;
    22 }
    23 int main(){
    24     scanf("%d%d",&n,&m);
    25     for(int i=1;i<=n;++i) ++l[read()],++r[read()];
    26     for(int i=1;i<=m;++i) l[i]+=l[i-1],r[i]+=r[i-1];
    27     js[0]=1;
    28     for(int i=1;i<=m;++i) js[i]=1ll*js[i-1]*i%mod;
    29     jsinv[m]=qpow(js[m],mod-2);
    30     for(int i=m;i;--i) jsinv[i-1]=1ll*jsinv[i]*i%mod;
    31     dp[0][0]=1;
    32     for(int i=1;i<=m;++i){
    33         dp[i][0]=1ll*dp[i-1][0]*A(i-l[i-1],l[i]-l[i-1])%mod;
    34         for(int j=1;j<=min(i,n);++j) dp[i][j]=(dp[i-1][j]+1ll*dp[i-1][j-1]*max(r[i]-j+1,0)%mod)*A(i-j-l[i-1],l[i]-l[i-1])%mod;
    35     }
    36     printf("%d
    ",dp[m][n]);
    37     return 0;
    38 }
    View Code

     

     

     

    C. big

    考试时打了显然的暴力。

    最后十分钟,想到了对x的操作可以转化为逻辑左移一位。

    问题转化为取一个x,使得$((x$^$pre_i)<<1)$^$suf_{i+1}$取得最大值。

    显然<<1可以拆开,又因为x<<1和x之间的映射是一一对应的。

    问题又转化为取一个x,使得$x$^$((pre_i<<1)$^$suf_{i+1})$最大。

    后者可以合并,使用trie树查询最大值和方案数。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 using namespace std;
     5 const int M=100010;
     6 int n,m,s[M],suf[M],ans,cnt;
     7 struct trie{
     8     struct node{
     9         node *ch[2];
    10     }*root,pool[M*30],*ptr=pool;
    11     void build(){
    12         root=ptr++;
    13     }
    14     void insert(int x){
    15         node *now=root;
    16         for(int i=n-1;~i;--i){
    17             if(!now->ch[x>>i&1]) now->ch[x>>i&1]=ptr++;
    18             now=now->ch[x>>i&1];
    19         }
    20     }
    21     void dfs(node *now,int k,int num){
    22         if(now->ch[0]&&now->ch[1]){
    23             dfs(now->ch[0],k-1,num);
    24             dfs(now->ch[1],k-1,num);
    25         }
    26         else if(now->ch[0]) dfs(now->ch[0],k-1,num+(1<<k-1));
    27         else if(now->ch[1]) dfs(now->ch[1],k-1,num+(1<<k-1));
    28         else{
    29             if(num>ans) ans=num,cnt=1;
    30             else if(num==ans) ++cnt;
    31         }
    32     }
    33 }tr;
    34 inline int turn(int x){
    35     return (x>>n-1&1)+(x<<1)&(1<<n)-1;//*/ +- <<>> & | ^
    36 }
    37 int main(){
    38     scanf("%d%d",&n,&m);
    39     for(int i=1;i<=m;++i) scanf("%d",&s[i]);
    40     for(int i=m;i;--i) suf[i]=suf[i+1]^s[i];
    41     tr.build();
    42     tr.insert(suf[1]);
    43     for(int i=1,pre=0;i<=m;++i){
    44         pre^=s[i];
    45         tr.insert(turn(pre)^suf[i+1]);
    46     }
    47     tr.dfs(tr.root,n,0);
    48     printf("%d
    %d
    ",ans,cnt);
    49     return 0;
    50 }
    View Code
  • 相关阅读:
    快速分栏
    伪元素:before和:after的简单应用——清除浮动
    C# 调用事件
    C# 创建文件夹
    (C#-VisionPro)用代码方式新建VisionPro视觉文件(.vpp)
    C# 选择下拉框文件时触发
    C# 将指定目录下的文件夹名称加载到下拉框中
    (C#-VisionPro)用C#加载、保存ViaionPro的'.vpp'文件
    C# 关闭程序时保存程序数据
    (C#-VisionPro)用C#调用VisionPro保存的.vpp文件
  • 原文地址:https://www.cnblogs.com/skyh/p/11286749.html
Copyright © 2020-2023  润新知