[NOIP模拟测试11] 题解

A.string

和河北的一道省选题很像。考场上写的暴力桶排,正解其实就是优化一下这个思路。

开线段树维护字符串中每个字母出现的次数。对于每条询问,区间查询、区间赋值维护即可。

另外,本题卡常严重,正解能被卡到40到90不等。所以直接循环展开乱搞一波?

暴力40分代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int a[N],bu[205],n,m;
char str[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
void busort(int op,int l,int r)
{
    for(int i=l;i<=r;i++)
        bu[a[i]]++;
    int tot=0;
    if(op)
    {
        for(int i=97;i<=122;i++)
            while(bu[i])a[l+(tot++)]=i,bu[i]--;
    }
    else
    {
        for(int i=122;i>=97;i--)
            while(bu[i])a[l+(tot++)]=i,bu[i]--;
    }
}
int main()
{
    n=read();m=read();
    scanf("%s",str+1);
    for(int i=1;i<=n;i++)
        a[i]=(int)str[i];
    while(m--)
    {
        int l=read(),r=read(),op=read();
        busort(op,l,r);
    }
    for(int i=1;i<=n;i++)
    {
        char ch=(char)a[i];
        printf("%c",ch);
    }
    return 0;
}

被卡的90分代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,len;
char str[N],ans[N];
#define ls(k) k<<1
#define rs(k) k<<1|1
int num[N<<2][28],lz[N<<2],le[N<<2],re[N<<2];
int res[28];
void up(int k)
{
    for(int i=1;i<=26;i++)
        num[k][i]=num[ls(k)][i]+num[rs(k)][i];
}
void build(int k,int l,int r)
{
    lz[k]=0;le[k]=l,re[k]=r;
    if(l==r)
    {
        num[k][str[l]-'a'+1]=1;
        return ;
    }
    int mid=l+r>>1;
    build(ls(k),l,mid);
    build(rs(k),mid+1,r);
    up(k);
}
void down(int k)
{
    memset(num[ls(k)],0,sizeof(num[ls(k)]));
    memset(num[rs(k)],0,sizeof(num[rs(k)]));
    num[ls(k)][lz[k]]=re[ls(k)]-le[ls(k)]+1;
    num[rs(k)][lz[k]]=re[rs(k)]-le[rs(k)]+1;
    lz[ls(k)]=lz[rs(k)]=lz[k];
    lz[k]=0;
}
void query(int k,int L,int R)
{
    if(le[k]>=L&&R>=re[k])
    {
        for(int i=1;i<=26;i++)
            res[i]+=num[k][i];
        return ;
    }
    int mid=le[k]+re[k]>>1;
    if(lz[k])down(k);
    if(L<=mid)query(ls(k),L,R);
    if(R>mid)query(rs(k),L,R);
}
void change(int k,int L,int R,int val)
{
    if(le[k]>=L&&R>=re[k])
    {
        memset(num[k],0,sizeof(num[k]));
        num[k][val]=re[k]-le[k]+1;
        lz[k]=val;
        return ;
    }
    int mid=le[k]+re[k]>>1;
    if(lz[k])down(k);
    if(L<=mid)change(ls(k),L,R,val);
    if(R>mid)change(rs(k),L,R,val);
    up(k);
}
void cacl(int k)
{
    if(le[k]==re[k])
    {
        for(int i=1;i<=26;i++)
            if(num[k][i])
            {
                ans[++len]=i-1+'a';
                return ;
            }
    }
    if(lz[k])down(k);
    cacl(ls(k));
    cacl(rs(k));
}
int main()
{
    n=read();m=read();
    scanf("%s",str+1);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read(),op=read();
        memset(res,0,sizeof(res));
        query(1,l,r);
        if(op)
        {
            for(int j=1;j<=26;j++)
                if(res[j])
                    change(1,l,l+res[j]-1,j),l+=res[j];
        }
        else
        {
            for(int j=26;j>=1;j--)
                if(res[j])
                    change(1,l,l+res[j]-1,j),l+=res[j];
        }
    }
    cacl(1);
    printf("%s
",ans+1);
    return 0;
}

丧心病狂科学打表的循环展开代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define rint  register int
using namespace std;
const int N=100005;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,len;
char str[N],ans[N];
#define ls(k) k<<1
#define rs(k) k<<1|1
int num[N<<2][28],lz[N<<2],le[N<<2],re[N<<2];
int res[28];
void up(int k)
{
                                 num[k][1]=num[ls(k)][1]+num[rs(k)][1];
                     num[k][2]=num[ls(k)][2]+num[rs(k)][2];
                                                 num[k][3]=num[ls(k)][3]+num[rs(k)][3];
                  num[k][4]=num[ls(k)][4]+num[rs(k)][4];
                                                          num[k][5]=num[ls(k)][5]+num[rs(k)][5];
                      num[k][6]=num[ls(k)][6]+num[rs(k)][6];
                                                                   num[k][7]=num[ls(k)][7]+num[rs(k)][7];
                              num[k][8]=num[ls(k)][8]+num[rs(k)][8];
      num[k][9]=num[ls(k)][9]+num[rs(k)][9];
                                      num[k][10]=num[ls(k)][10]+num[rs(k)][10];
                                                                 num[k][11]=num[ls(k)][11]+num[rs(k)][11];
                                          num[k][12]=num[ls(k)][12]+num[rs(k)][12];
                                      num[k][13]=num[ls(k)][13]+num[rs(k)][13];
                                                                          num[k][14]=num[ls(k)][14]+num[rs(k)][14];
                                 num[k][15]=num[ls(k)][15]+num[rs(k)][15];
                                                              num[k][16]=num[ls(k)][16]+num[rs(k)][16];
                         num[k][17]=num[ls(k)][17]+num[rs(k)][17];
                                             num[k][18]=num[ls(k)][18]+num[rs(k)][18];
                                                                     num[k][19]=num[ls(k)][19]+num[rs(k)][19];
              num[k][20]=num[ls(k)][20]+num[rs(k)][20];
                                                                  num[k][21]=num[ls(k)][21]+num[rs(k)][21];
                                  num[k][22]=num[ls(k)][22]+num[rs(k)][22];
          num[k][23]=num[ls(k)][23]+num[rs(k)][23];
                                                                 num[k][24]=num[ls(k)][24]+num[rs(k)][24];
                              num[k][25]=num[ls(k)][25]+num[rs(k)][25];
                                 num[k][26]=num[ls(k)][26]+num[rs(k)][26];
}
void build(int k,int l,int r)
{
    lz[k]=0;le[k]=l,re[k]=r;
    if(l==r)
    {
        num[k][str[l]-'a'+1]=1;
        return ;
    }
    int mid=l+r>>1;
    build(ls(k),l,mid);
    build(rs(k),mid+1,r);
    up(k);
}
void down(int k)
{
    memset(num[ls(k)],0,sizeof(num[ls(k)]));
    memset(num[rs(k)],0,sizeof(num[rs(k)]));
    num[ls(k)][lz[k]]=re[ls(k)]-le[ls(k)]+1;
    num[rs(k)][lz[k]]=re[rs(k)]-le[rs(k)]+1;
    lz[ls(k)]=lz[rs(k)]=lz[k];
    lz[k]=0;
}
void query(int k,int L,int R)
{
    if(le[k]>=L&&R>=re[k])
    {
                                                                res[1]+=num[k][1];
                                                    res[2]+=num[k][2];
            res[3]+=num[k][3];
                        res[4]+=num[k][4];
                        res[5]+=num[k][5];
            res[6]+=num[k][6];
                            res[7]+=num[k][7];
                                    res[8]+=num[k][8];
                res[9]+=num[k][9];
                                    res[10]+=num[k][10];
                        res[11]+=num[k][11];
                                                res[12]+=num[k][12];
                    res[13]+=num[k][13];
                                                            res[14]+=num[k][14];
                                res[15]+=num[k][15];
                                                res[16]+=num[k][16];
                                            res[17]+=num[k][17];
                                                        res[18]+=num[k][18];
                                res[19]+=num[k][19];
                                            res[20]+=num[k][20];
                res[21]+=num[k][21];
        res[22]+=num[k][22];
                    res[23]+=num[k][23];
                                        res[24]+=num[k][24];
                                                            res[25]+=num[k][25];
                                    res[26]+=num[k][26];
        return ;
    }
    int mid=le[k]+re[k]>>1;
    if(lz[k])down(k);
    if(L<=mid)query(ls(k),L,R);
    if(R>mid)query(rs(k),L,R);
}
void change(int k,int L,int R,int val)
{
    if(le[k]>=L&&R>=re[k])
    {
        memset(num[k],0,sizeof(num[k]));
        num[k][val]=re[k]-le[k]+1;
        lz[k]=val;
        return ;
    }
    int mid=le[k]+re[k]>>1;
    if(lz[k])down(k);
    if(L<=mid)change(ls(k),L,R,val);
    if(R>mid)change(rs(k),L,R,val);
    up(k);
}
void cacl(int k)
{
    if(le[k]==re[k])
    {
        if(num[k][1]){ans[++len]=1-1+'a';return ;}
                        if(num[k][2]){ans[++len]=2-1+'a';return ;}
                if(num[k][3]){ans[++len]=3-1+'a';return ;}
                            if(num[k][4]){ans[++len]=4-1+'a';return ;}
                                        if(num[k][5]){ans[++len]=5-1+'a';return ;}
    if(num[k][6]){ans[++len]=6-1+'a';return ;}
                if(num[k][7]){ans[++len]=7-1+'a';return ;}
                                    if(num[k][8]){ans[++len]=8-1+'a';return ;}
                                            if(num[k][9]){ans[++len]=9-1+'a';return ;}
    if(num[k][10]){ans[++len]=10-1+'a';return ;}
                                        if(num[k][11]){ans[++len]=11-1+'a';return ;}
                        if(num[k][12]){ans[++len]=12-1+'a';return ;}
    if(num[k][13]){ans[++len]=13-1+'a';return ;}
    if(num[k][14]){ans[++len]=14-1+'a';return ;}
            if(num[k][15]){ans[++len]=15-1+'a';return ;}
                        if(num[k][16]){ans[++len]=16-1+'a';return ;}
                                    if(num[k][17]){ans[++len]=17-1+'a';return ;}
    if(num[k][18]){ans[++len]=18-1+'a';return ;}
                                if(num[k][19]){ans[++len]=19-1+'a';return ;}
                        if(num[k][20]){ans[++len]=20-1+'a';return ;}
                                                    if(num[k][21]){ans[++len]=21-1+'a';return ;}
                            if(num[k][22]){ans[++len]=22-1+'a';return ;}
                if(num[k][23]){ans[++len]=23-1+'a';return ;}
    if(num[k][24]){ans[++len]=24-1+'a';return ;}
            if(num[k][25]){ans[++len]=25-1+'a';return ;}
                                    if(num[k][26]){ans[++len]=26-1+'a';return ;}
    }
    if(lz[k])down(k);
    cacl(ls(k));
    cacl(rs(k));
}
int main()
{
    n=read();m=read();
    scanf("%s",str+1);
    build(1,1,n);
    for(rint i=1;i<=m;i++)
    {
        int l=read(),r=read(),op=read();
        memset(res,0,sizeof(res));
        query(1,l,r);
        if(op)
        {
                        if(res[1])change(1,l,l+res[1]-1,1),l+=res[1];
                    if(res[2])change(1,l,l+res[2]-1,2),l+=res[2];
                                if(res[3])change(1,l,l+res[3]-1,3),l+=res[3];
                    if(res[4])change(1,l,l+res[4]-1,4),l+=res[4];
                                                if(res[5])change(1,l,l+res[5]-1,5),l+=res[5];
                        if(res[6])change(1,l,l+res[6]-1,6),l+=res[6];
                                            if(res[7])change(1,l,l+res[7]-1,7),l+=res[7];
                            if(res[8])change(1,l,l+res[8]-1,8),l+=res[8];
                                                    if(res[9])change(1,l,l+res[9]-1,9),l+=res[9];
                        if(res[10])change(1,l,l+res[10]-1,10),l+=res[10];
                                            if(res[11])change(1,l,l+res[11]-1,11),l+=res[11];
                            if(res[12])change(1,l,l+res[12]-1,12),l+=res[12];
                                                if(res[13])change(1,l,l+res[13]-1,13),l+=res[13];
                                if(res[14])change(1,l,l+res[14]-1,14),l+=res[14];
                                                    if(res[15])change(1,l,l+res[15]-1,15),l+=res[15];
                if(res[16])change(1,l,l+res[16]-1,16),l+=res[16];
                                if(res[17])change(1,l,l+res[17]-1,17),l+=res[17];
                                        if(res[18])change(1,l,l+res[18]-1,18),l+=res[18];
                    if(res[19])change(1,l,l+res[19]-1,19),l+=res[19];
                                                if(res[20])change(1,l,l+res[20]-1,20),l+=res[20];
                        if(res[21])change(1,l,l+res[21]-1,21),l+=res[21];
                                                    if(res[22])change(1,l,l+res[22]-1,22),l+=res[22];
                        if(res[23])change(1,l,l+res[23]-1,23),l+=res[23];
                                        if(res[24])change(1,l,l+res[24]-1,24),l+=res[24];
                if(res[25])change(1,l,l+res[25]-1,25),l+=res[25];
                                   if(res[26])change(1,l,l+res[26]-1,26),l+=res[26];
        }
        else
        {
                                if(res[26])change(1,l,l+res[26]-1,26),l+=res[26];
        if(res[25])change(1,l,l+res[25]-1,25),l+=res[25];
                                            if(res[24])change(1,l,l+res[24]-1,24),l+=res[24];
                    if(res[23])change(1,l,l+res[23]-1,23),l+=res[23];
                                        if(res[22])change(1,l,l+res[22]-1,22),l+=res[22];
            if(res[21])change(1,l,l+res[21]-1,21),l+=res[21];
                                        if(res[20])change(1,l,l+res[20]-1,20),l+=res[20];
      if(res[19])change(1,l,l+res[19]-1,19),l+=res[19];
                                        if(res[18])change(1,l,l+res[18]-1,18),l+=res[18];
                            if(res[17])change(1,l,l+res[17]-1,17),l+=res[17];
                                                    if(res[16])change(1,l,l+res[16]-1,16),l+=res[16];
                        if(res[15])change(1,l,l+res[15]-1,15),l+=res[15];
                                            if(res[14])change(1,l,l+res[14]-1,14),l+=res[14];
                                        if(res[13])change(1,l,l+res[13]-1,13),l+=res[13];
                            if(res[12])change(1,l,l+res[12]-1,12),l+=res[12];
                                                        if(res[11])change(1,l,l+res[11]-1,11),l+=res[11];
                                        if(res[10])change(1,l,l+res[10]-1,10),l+=res[10];
                                    if(res[9])change(1,l,l+res[9]-1,9),l+=res[9];
                            if(res[8])change(1,l,l+res[8]-1,8),l+=res[8];
                    if(res[7])change(1,l,l+res[7]-1,7),l+=res[7];
                                        if(res[6])change(1,l,l+res[6]-1,6),l+=res[6];
                                if(res[5])change(1,l,l+res[5]-1,5),l+=res[5];
                                            if(res[4])change(1,l,l+res[4]-1,4),l+=res[4];
                                        if(res[3])change(1,l,l+res[3]-1,3),l+=res[3];
       if(res[2])change(1,l,l+res[2]-1,2),l+=res[2];
                            if(res[1])change(1,l,l+res[1]-1,1),l+=res[1];
        }
    }
    cacl(1);
    printf("%s
",ans+1);
    return 0;
}

B.matrix

神dp。状态数组我估计一辈子都想不出来。

(虽然题意不明 似乎默认限制区间外必填0?)

我们把构造矩阵的过程看作往矩阵里填1的过程,设$f[i][j]$为填到第i列,有j个1在右侧限制区间里的方案数。

设$lnum[i]$为到i列时已结束的左限制区间的个数,$rnum[i]$为到i列时已开始的右限制区间的个数。

转移时左右区间分别考虑:

对于右区间,考虑到i列放与不放即可。如果不放的话就直接继承状态,即$f[i][j]+=f[i-1][j]$.如果放,则有$f[i][j+1]+=f[i-1][j]*(rnum[i]-j)$,解释一下:放1肯定得在已开始的右区间里放,但是还要除去已经放了的j个右区间。

对于左区间,我们只在恰好经过它的结束点时对它进行考虑。既然有j个1放在右限制区间,已经转移了i列,那么一定有i-j个1放在左限制区间,毕竟每列只放一个。又因为有$lnum[i-1]$个左区间已经结束并处理过了,所以留给恰在第i列结束的左区间的1只有$i-j-lnum[i-1]$个(当然,这些1也可以放在之前的某一列里),那么对方案的贡献就要乘上$A_{i-j-lnum[i-1]}^{lnum[i]-lnum[i-1]}$了。

排列数打表或逆元求皆可。

//dp[i][j]:到第i列,j个1在各行的右区间
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=3005;
const ll mod=998244353;
int L[N],R[N],numl[N],numr[N],n,m;
ll dp[N][N],A[N][N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&L[i],&R[i]);
        numl[L[i]]++;numr[R[i]]++;
    }
    for(int i=1;i<=m;i++)
        numl[i]+=numl[i-1],numr[i]+=numr[i-1];
    for(int i=1;i<=m;i++)
    {
        A[i][0]=1;
        for(int j=1;j<=i;j++)
            A[i][j]=A[i][j-1]*1LL*(i-j+1)%mod;
    }
    dp[1][0]=1;A[0][0]=1;
    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<=n;j++)
        {
            if(i-j-numl[i-1]<numl[i]-numl[i-1])//左:在这一列结束的左区间能放几个1
            {
                dp[i][j]=0;
                continue;
            }
            (dp[i][j]*=A[i-j-numl[i-1]][numl[i]-numl[i-1]])%=mod;
            (dp[i+1][j]+=dp[i][j])%=mod;
            (dp[i+1][j+1]+=dp[i][j]*1LL*(numr[i+1]-j))%=mod;
        //    cout<<dp[i][j]<<endl;
        }
    }
    cout<<dp[m][n]<<endl;
    return 0;
}

C.

sb对手干的破事其实就是循环左移(最靠前的一位挪到后面,剩下全体左移一位)

异或满足交换律结合律,那么就可以先利用前后缀求出每次除了初值外的异或和,将它们插入一棵01Trie,并通过对它进行dfs得出结果。

如果儿子只有一个,我们选x时就能取与它相反的一位从而让这位成为1,所以在计答案时这一位为1;如果儿子有两个,不论我们这位选0还是1,对手都会取相同的一位使这一位成为0,所以答案中这一位只能为0。

复杂度$O(nm)$

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int a[N],trie[N*26][3],xors[N],sxor[N];
int tot,ans,ansnum,n,m;
void ins(int num)
{
    int root=0;
    for(int i=n-1;i>=0;i--)
    {
        int x=(num>>i)&1;
        if(!trie[root][x])trie[root][x]=++tot;
        root=trie[root][x];
    }
}
void dfs(int x,int val,int pos)
{
    if(pos==1)
    {
        if(val>ans)ans=val,ansnum=1;
        else if(val==ans)ansnum++;
        return ;
    }
    if(!trie[x][0]||!trie[x][1])dfs(trie[x][0]+trie[x][1],val|(pos>>1),pos>>1);
    else dfs(trie[x][0],val,pos>>1),dfs(trie[x][1],val,pos>>1);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    sxor[1]=a[1];
    for(int i=2;i<=m;i++)
        sxor[i]=sxor[i-1]^a[i];
    xors[m]=a[m];
    for(int i=m-1;i>=1;i--)
        xors[i]=xors[i+1]^a[i];
    for(int i=0;i<=m;i++)
        ins(xors[i+1]^(((sxor[i]<<1)/(1<<n)+(sxor[i]<<1))%(1<<n)));
    dfs(0,0,1<<n);
    cout<<ans<<endl<<ansnum<<endl;
    return 0;
}

UPD:之前说T3那个式子叫逻辑左移?不对不对,被亚历克斯鹅科普了一下,应该是循环左移(意会为主好吧)。

原文地址:https://www.cnblogs.com/Rorschach-XR/p/11288573.html